@cleocode/lafs-protocol 0.1.1 → 1.0.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 +129 -23
- package/dist/examples/discovery-server.d.ts +8 -0
- package/dist/examples/discovery-server.js +216 -0
- package/dist/examples/mcp-lafs-client.d.ts +10 -0
- package/dist/examples/mcp-lafs-client.js +427 -0
- package/dist/examples/mcp-lafs-server.d.ts +10 -0
- package/dist/examples/mcp-lafs-server.js +358 -0
- package/dist/schemas/v1/envelope.schema.json +103 -14
- package/dist/schemas/v1/error-registry.json +19 -1
- package/dist/src/budgetEnforcement.d.ts +84 -0
- package/dist/src/budgetEnforcement.js +328 -0
- package/dist/src/cli.d.ts +14 -0
- package/dist/src/cli.js +14 -0
- package/dist/src/conformance.js +80 -7
- package/dist/src/discovery.d.ts +127 -0
- package/dist/src/discovery.js +304 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +4 -0
- package/dist/src/mcpAdapter.d.ts +28 -0
- package/dist/src/mcpAdapter.js +281 -0
- package/dist/src/tokenEstimator.d.ts +87 -0
- package/dist/src/tokenEstimator.js +238 -0
- package/dist/src/types.d.ts +67 -7
- package/lafs.md +331 -23
- package/package.json +8 -3
- package/schemas/v1/context-ledger.schema.json +70 -0
- package/schemas/v1/discovery.schema.json +132 -0
- package/schemas/v1/envelope.schema.json +103 -14
- package/schemas/v1/error-registry.json +19 -1
package/lafs.md
CHANGED
|
@@ -2,19 +2,41 @@
|
|
|
2
2
|
|
|
3
3
|
## 1. Scope
|
|
4
4
|
|
|
5
|
-
LAFS
|
|
5
|
+
LAFS is a **response envelope contract specification**. It defines the canonical shape of structured responses — success envelopes, error envelopes, pagination metadata, and context preservation — for software systems whose primary consumer is an LLM agent or AI-driven tool.
|
|
6
6
|
|
|
7
|
-
LAFS is
|
|
7
|
+
LAFS is **not** a protocol, framework, or runtime. It specifies **what** a conformant response looks like, not how that response is transported or generated. Implementations MAY deliver LAFS envelopes over HTTP, gRPC, CLI, SDK interfaces, message queues, or any other transport mechanism. LAFS is transport-agnostic and language-agnostic.
|
|
8
|
+
|
|
9
|
+
LAFS is designed to complement — not compete with — existing agent and tool-integration protocols. The Model Context Protocol (MCP) defines how LLM hosts discover and invoke tools; the Agent-to-Agent protocol (A2A) defines how autonomous agents communicate and delegate tasks. LAFS operates at a different layer: it standardizes the **response contract** that tools and agents SHOULD return, regardless of the protocol used to invoke them. An MCP tool server, an A2A agent, or a plain REST API MAY all return LAFS-conformant envelopes.
|
|
10
|
+
|
|
11
|
+
While LAFS is purpose-built for AI and LLM tool ecosystems — where deterministic, machine-parseable responses are critical — the specification is generally applicable to any API that benefits from structured, predictable response contracts.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 2. Non-Goals
|
|
16
|
+
|
|
17
|
+
The following capabilities are intentionally outside the scope of LAFS. This section exists to prevent scope creep and to clarify boundaries with complementary protocols.
|
|
18
|
+
|
|
19
|
+
1. **Streaming responses.** LAFS defines discrete request/response envelopes. Streaming mechanisms such as SSE or WebSocket are transport concerns and MUST NOT be defined by LAFS.
|
|
20
|
+
|
|
21
|
+
2. **Asynchronous processing.** LAFS envelopes are synchronous response contracts. Async job patterns (polling, webhooks, callback queues) are application-layer concerns and are outside LAFS scope.
|
|
22
|
+
|
|
23
|
+
3. **Authentication and authorization.** LAFS is transport-agnostic; auth is a transport or middleware concern. LAFS MAY carry auth-related error codes (e.g., `E_AUTH_*`) but MUST NOT define authentication or authorization flows.
|
|
24
|
+
|
|
25
|
+
4. **Multi-modal content.** LAFS envelopes carry structured JSON data. Binary payloads, media content negotiation, and multi-modal encoding are outside scope.
|
|
26
|
+
|
|
27
|
+
5. **Transport binding.** LAFS defines the response envelope shape, not how it maps to HTTP status codes, gRPC metadata, or other transport semantics. Transport mapping specifications are a separate concern.
|
|
28
|
+
|
|
29
|
+
6. **Service discovery.** LAFS does not define how consumers locate or enumerate LAFS-conformant endpoints. Discovery mechanisms SHOULD be provided by the deployment layer or complementary protocols.
|
|
8
30
|
|
|
9
31
|
---
|
|
10
32
|
|
|
11
|
-
##
|
|
33
|
+
## 3. RFC 2119 Keywords
|
|
12
34
|
|
|
13
35
|
The keywords MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are interpreted per RFC 2119.
|
|
14
36
|
|
|
15
37
|
---
|
|
16
38
|
|
|
17
|
-
##
|
|
39
|
+
## 4. Non-Negotiable Protocol Rules
|
|
18
40
|
|
|
19
41
|
1. Output default MUST be machine-readable JSON.
|
|
20
42
|
2. Human-readable mode MUST be explicit opt-in.
|
|
@@ -25,9 +47,9 @@ The keywords MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are interpreted per RFC
|
|
|
25
47
|
|
|
26
48
|
---
|
|
27
49
|
|
|
28
|
-
##
|
|
50
|
+
## 5. Format Semantics
|
|
29
51
|
|
|
30
|
-
###
|
|
52
|
+
### 5.1 Required output semantics
|
|
31
53
|
|
|
32
54
|
- Default format MUST be `json`.
|
|
33
55
|
- `--human` MUST switch output mode to human-readable.
|
|
@@ -35,7 +57,7 @@ The keywords MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are interpreted per RFC
|
|
|
35
57
|
- Providing both `--human` and `--json` MUST fail with `E_FORMAT_CONFLICT`.
|
|
36
58
|
- Explicit flags MUST override env/config defaults.
|
|
37
59
|
|
|
38
|
-
###
|
|
60
|
+
### 5.2 Recommended precedence
|
|
39
61
|
|
|
40
62
|
1. Explicit CLI/API request value
|
|
41
63
|
2. Project config
|
|
@@ -44,7 +66,7 @@ The keywords MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are interpreted per RFC
|
|
|
44
66
|
|
|
45
67
|
---
|
|
46
68
|
|
|
47
|
-
##
|
|
69
|
+
## 6. Canonical Response Envelope
|
|
48
70
|
|
|
49
71
|
All responses MUST conform to `schemas/v1/envelope.schema.json`.
|
|
50
72
|
|
|
@@ -69,16 +91,25 @@ All responses MUST conform to `schemas/v1/envelope.schema.json`.
|
|
|
69
91
|
}
|
|
70
92
|
```
|
|
71
93
|
|
|
72
|
-
###
|
|
94
|
+
### 6.1 Envelope invariants
|
|
73
95
|
|
|
74
96
|
- Exactly one of `result` or `error` MUST be non-null.
|
|
75
|
-
- `success=true` implies `error=null
|
|
76
|
-
- `success=false` implies `result=null
|
|
97
|
+
- `success=true` implies `error=null` or error omitted.
|
|
98
|
+
- `success=false` implies `result=null` and `error` MUST be present.
|
|
99
|
+
- The `page` and `error` fields are optional when their value would be null. In strict mode, producers SHOULD omit these fields rather than set them to null.
|
|
77
100
|
- Unknown fields SHOULD be rejected when strict mode is enabled.
|
|
78
101
|
|
|
102
|
+
### 6.2 Extensions
|
|
103
|
+
|
|
104
|
+
The envelope supports an optional `_extensions` object for vendor-specific metadata. Because `_extensions` is a declared property in the schema, it is permitted regardless of strict mode.
|
|
105
|
+
|
|
106
|
+
- Keys SHOULD use the `x-` prefix convention (e.g., `x-myvendor-trace-id`).
|
|
107
|
+
- Consumers MUST NOT rely on extension fields for protocol-required behavior.
|
|
108
|
+
- Producers MAY omit `_extensions` entirely; the field is always optional.
|
|
109
|
+
|
|
79
110
|
---
|
|
80
111
|
|
|
81
|
-
##
|
|
112
|
+
## 7. Error Contract
|
|
82
113
|
|
|
83
114
|
Errors MUST conform to envelope `error` shape and use codes from `schemas/v1/error-registry.json`.
|
|
84
115
|
|
|
@@ -95,7 +126,30 @@ Errors MUST conform to envelope `error` shape and use codes from `schemas/v1/err
|
|
|
95
126
|
}
|
|
96
127
|
```
|
|
97
128
|
|
|
98
|
-
###
|
|
129
|
+
### 7.1 Error code naming convention
|
|
130
|
+
|
|
131
|
+
Error codes MUST match the pattern: `^E_[A-Z0-9]+_[A-Z0-9_]+$`
|
|
132
|
+
|
|
133
|
+
The structure is **E\_\<DOMAIN\>\_\<SPECIFIC\>**, where:
|
|
134
|
+
|
|
135
|
+
- **E\_** — required prefix identifying the value as an error code.
|
|
136
|
+
- **DOMAIN** — a short uppercase token describing the error's semantic area (e.g., `VALIDATION`, `CONTEXT`, `RATE`, `MIGRATION`). The domain is descriptive; it does not need to equal the `category` enum value.
|
|
137
|
+
- **SPECIFIC** — one or more uppercase tokens (separated by `_`) that distinguish the error within its domain (e.g., `SCHEMA`, `MISSING`, `UNSUPPORTED_VERSION`).
|
|
138
|
+
|
|
139
|
+
Examples from the registry:
|
|
140
|
+
|
|
141
|
+
| Code | Domain | Specific | Category |
|
|
142
|
+
|---|---|---|---|
|
|
143
|
+
| `E_VALIDATION_SCHEMA` | `VALIDATION` | `SCHEMA` | VALIDATION |
|
|
144
|
+
| `E_NOT_FOUND_RESOURCE` | `NOT` | `FOUND_RESOURCE` | NOT_FOUND |
|
|
145
|
+
| `E_CONTEXT_MISSING` | `CONTEXT` | `MISSING` | CONTRACT |
|
|
146
|
+
| `E_MIGRATION_UNSUPPORTED_VERSION` | `MIGRATION` | `UNSUPPORTED_VERSION` | MIGRATION |
|
|
147
|
+
|
|
148
|
+
Registered categories (the `category` field in error objects): `VALIDATION`, `AUTH`, `PERMISSION`, `NOT_FOUND`, `CONFLICT`, `RATE_LIMIT`, `TRANSIENT`, `INTERNAL`, `CONTRACT`, `MIGRATION`.
|
|
149
|
+
|
|
150
|
+
Custom error codes MUST match the same regex pattern. Implementations SHOULD choose a domain token that clearly communicates the error's origin.
|
|
151
|
+
|
|
152
|
+
### 7.2 Required behavior
|
|
99
153
|
|
|
100
154
|
- Error codes MUST be stable within major versions.
|
|
101
155
|
- Retry semantics MUST be encoded in `retryable` and `retryAfterMs`.
|
|
@@ -103,7 +157,7 @@ Errors MUST conform to envelope `error` shape and use codes from `schemas/v1/err
|
|
|
103
157
|
|
|
104
158
|
---
|
|
105
159
|
|
|
106
|
-
##
|
|
160
|
+
## 8. Context Preservation
|
|
107
161
|
|
|
108
162
|
Multi-step operations MUST preserve a context ledger with at least:
|
|
109
163
|
|
|
@@ -122,30 +176,209 @@ Rules:
|
|
|
122
176
|
- Decisions affecting output MUST be represented in ledger state.
|
|
123
177
|
- Missing required context for a mutating step MUST fail with structured error.
|
|
124
178
|
|
|
179
|
+
### 8.1 Context Retrieval
|
|
180
|
+
|
|
181
|
+
Agents MAY retrieve context ledger state via `GET /_lafs/context/{ledgerId}` with projection modes.
|
|
182
|
+
|
|
183
|
+
#### 8.1.1 Projection Modes
|
|
184
|
+
|
|
185
|
+
**Full Mode (`mode=full`):**
|
|
186
|
+
Returns complete ledger including all entries.
|
|
187
|
+
- Use for: Initial loads, recovery scenarios
|
|
188
|
+
- Supports: Offset-based pagination
|
|
189
|
+
- Response includes: All ledger fields
|
|
190
|
+
|
|
191
|
+
**Delta Mode (`mode=delta&sinceVersion=N`):**
|
|
192
|
+
Returns only entries added since version N.
|
|
193
|
+
- Use for: Active workflows (efficient sync)
|
|
194
|
+
- Response includes:
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"ledgerId": "ctx_abc123",
|
|
198
|
+
"mode": "delta",
|
|
199
|
+
"fromVersion": 10,
|
|
200
|
+
"toVersion": 15,
|
|
201
|
+
"entries": [/* new entries only */],
|
|
202
|
+
"removedConstraints": [/* constraints no longer active */],
|
|
203
|
+
"checksum": "sha256:..."
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Summary Mode (`mode=summary`):**
|
|
208
|
+
Returns checksum and version for validation.
|
|
209
|
+
- Use for: Quick sync validation
|
|
210
|
+
- Response includes only: `ledgerId`, `version`, `checksum`, `entryCount`
|
|
211
|
+
|
|
212
|
+
#### 8.1.2 Query Parameters
|
|
213
|
+
|
|
214
|
+
| Parameter | Type | Description |
|
|
215
|
+
|-----------|------|-------------|
|
|
216
|
+
| `mode` | enum | `full`, `delta`, `summary` |
|
|
217
|
+
| `sinceVersion` | integer | For delta mode: return entries after this version |
|
|
218
|
+
| `filterByOperation` | string[] | Filter entries by operation name(s) |
|
|
219
|
+
| `limit` | integer | Max entries (1-1000, default 100) |
|
|
220
|
+
| `includeChecksum` | boolean | Include integrity checksum (default true) |
|
|
221
|
+
|
|
222
|
+
#### 8.1.3 Agent Guidance
|
|
223
|
+
|
|
224
|
+
- **Initial load**: Use `mode=full` once
|
|
225
|
+
- **Active workflows**: Use `mode=delta` with last known version
|
|
226
|
+
- **Validation**: Use `mode=summary` to verify sync state
|
|
227
|
+
- **Default recommendation**: `delta` mode for agent-optimal behavior
|
|
228
|
+
|
|
125
229
|
---
|
|
126
230
|
|
|
127
|
-
##
|
|
231
|
+
## 9. MVI and Progressive Disclosure
|
|
128
232
|
|
|
129
|
-
###
|
|
233
|
+
### 9.1 MVI default
|
|
130
234
|
|
|
131
235
|
- Default list/batch outputs MUST only contain fields required for next action.
|
|
132
236
|
- Verbose fields SHOULD be omitted by default.
|
|
133
237
|
- Systems SHOULD publish operation-level MVI budgets.
|
|
134
238
|
|
|
135
|
-
###
|
|
239
|
+
### 9.2 Field selection (`_fields`)
|
|
240
|
+
|
|
241
|
+
Clients MAY request a subset of response fields via the `_fields` request parameter.
|
|
242
|
+
|
|
243
|
+
- `_fields` MUST be an array of strings identifying top-level result field names.
|
|
244
|
+
- When `_fields` is present, the server MUST return only the requested fields plus any MVI-required fields for the declared disclosure level.
|
|
245
|
+
- When `_fields` is absent, the server MUST return fields appropriate for the declared `_meta.mvi` disclosure level.
|
|
246
|
+
- If a requested field does not exist on the resource, the server SHOULD omit it silently (no error). Servers MAY include a warning in `_meta.warnings` for unknown fields.
|
|
247
|
+
- `_fields` MUST NOT affect envelope structural fields (`$schema`, `_meta`, `success`, `error`, `page`, `_extensions`); it applies only to the contents of `result`.
|
|
136
248
|
|
|
137
|
-
|
|
138
|
-
- Unknown expansion fields SHOULD fail validation.
|
|
249
|
+
### 9.3 Expansion mechanism (`_expand`)
|
|
139
250
|
|
|
140
|
-
|
|
251
|
+
Clients MAY request expanded/nested data via the `_expand` request parameter.
|
|
252
|
+
|
|
253
|
+
- `_expand` MUST be an array of strings identifying relationships or nested resources to include inline.
|
|
254
|
+
- When `_expand` is present, the server MUST resolve and inline the requested expansions within `result`.
|
|
255
|
+
- If a requested expansion field is not recognized, the server MUST return error code `E_DISCLOSURE_UNKNOWN_FIELD` with category `VALIDATION`.
|
|
256
|
+
- Servers SHOULD document available expansion fields per operation.
|
|
257
|
+
- Expansion depth MUST be limited to prevent unbounded recursion. Servers SHOULD enforce a maximum expansion depth and return `E_MVI_BUDGET_EXCEEDED` if exceeded.
|
|
258
|
+
|
|
259
|
+
### 9.4 Pagination
|
|
141
260
|
|
|
142
261
|
- List operations SHOULD return deterministic `page` metadata.
|
|
143
262
|
- Pagination mode (offset or cursor) MUST be documented.
|
|
144
263
|
- Mixed pagination modes in one request MUST fail validation.
|
|
145
264
|
|
|
265
|
+
### 9.5 Token Budget Signaling
|
|
266
|
+
|
|
267
|
+
Token budget signaling enables clients to declare resource constraints that servers MUST respect when generating responses. This mechanism prevents context window overflow in LLM-driven workflows.
|
|
268
|
+
|
|
269
|
+
#### 9.5.1 Budget Declaration (`_budget`)
|
|
270
|
+
|
|
271
|
+
Clients MAY declare resource constraints via the `_budget` request parameter:
|
|
272
|
+
|
|
273
|
+
```json
|
|
274
|
+
{
|
|
275
|
+
"_budget": {
|
|
276
|
+
"maxTokens": 4000,
|
|
277
|
+
"maxBytes": 32768,
|
|
278
|
+
"maxItems": 100
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Fields:**
|
|
284
|
+
- `maxTokens` (integer) - Maximum approximate tokens
|
|
285
|
+
- `maxBytes` (integer) - Maximum byte size
|
|
286
|
+
- `maxItems` (integer) - Maximum items in lists
|
|
287
|
+
|
|
288
|
+
**Constraints:**
|
|
289
|
+
- At least one field MUST be present
|
|
290
|
+
- All values MUST be positive integers
|
|
291
|
+
- Servers MAY reject budgets exceeding implementation limits
|
|
292
|
+
|
|
293
|
+
#### 9.5.2 Server Behavior
|
|
294
|
+
|
|
295
|
+
Servers MUST:
|
|
296
|
+
1. Parse `_budget` from incoming requests
|
|
297
|
+
2. Estimate/measure response size
|
|
298
|
+
3. Return response within budget OR fail with `E_MVI_BUDGET_EXCEEDED`
|
|
299
|
+
|
|
300
|
+
Servers MAY truncate responses using:
|
|
301
|
+
- **Depth-first**: Remove deepest nested fields
|
|
302
|
+
- **Field priority**: Remove non-essential fields first
|
|
303
|
+
- **Hybrid**: Combine both strategies
|
|
304
|
+
|
|
305
|
+
When truncation occurs, servers MUST include:
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"_meta": {
|
|
309
|
+
"warnings": [{
|
|
310
|
+
"code": "E_MVI_BUDGET_TRUNCATED",
|
|
311
|
+
"message": "Response truncated to fit token budget"
|
|
312
|
+
}],
|
|
313
|
+
"_tokenEstimate": {
|
|
314
|
+
"estimated": 2847,
|
|
315
|
+
"budget": 4000,
|
|
316
|
+
"method": "character_based"
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
#### 9.5.3 Error Specification
|
|
323
|
+
|
|
324
|
+
**E_MVI_BUDGET_EXCEEDED:**
|
|
325
|
+
- **Category:** `VALIDATION`
|
|
326
|
+
- **Retryable:** `true`
|
|
327
|
+
- **Details:** `estimatedTokens`, `budget`, `excessTokens`, `constraint`
|
|
328
|
+
|
|
329
|
+
```json
|
|
330
|
+
{
|
|
331
|
+
"error": {
|
|
332
|
+
"code": "E_MVI_BUDGET_EXCEEDED",
|
|
333
|
+
"message": "Response exceeds declared token budget",
|
|
334
|
+
"category": "VALIDATION",
|
|
335
|
+
"retryable": true,
|
|
336
|
+
"details": {
|
|
337
|
+
"estimatedTokens": 5234,
|
|
338
|
+
"budget": 4000,
|
|
339
|
+
"excessTokens": 1234,
|
|
340
|
+
"constraint": "maxTokens"
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
#### 9.5.4 Token Estimation Algorithm (Normative)
|
|
347
|
+
|
|
348
|
+
Servers MUST implement this algorithm or equivalent (within +/- 10%):
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
FUNCTION estimate_tokens(value, depth = 0):
|
|
352
|
+
IF depth > 20: RETURN INFINITY
|
|
353
|
+
IF value IS null: RETURN 1
|
|
354
|
+
IF value IS boolean: RETURN 1
|
|
355
|
+
IF value IS number: RETURN max(1, len(stringify(value)) / 4)
|
|
356
|
+
IF value IS string:
|
|
357
|
+
graphemes = count_grapheme_clusters(value)
|
|
358
|
+
RETURN max(1, graphemes / 4.0)
|
|
359
|
+
IF value IS array:
|
|
360
|
+
tokens = 2 // []
|
|
361
|
+
FOR item IN value:
|
|
362
|
+
tokens += estimate_tokens(item, depth + 1) + 1
|
|
363
|
+
RETURN tokens
|
|
364
|
+
IF value IS object:
|
|
365
|
+
tokens = 2 // {}
|
|
366
|
+
FOR key, val IN value:
|
|
367
|
+
tokens += estimate_tokens(key, depth + 1)
|
|
368
|
+
tokens += 2 // : and ,
|
|
369
|
+
tokens += estimate_tokens(val, depth + 1)
|
|
370
|
+
RETURN tokens
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**Requirements:**
|
|
374
|
+
- Count grapheme clusters (not bytes) for unicode
|
|
375
|
+
- Enforce max depth of 20
|
|
376
|
+
- Handle circular references
|
|
377
|
+
- Complete within 10ms for 100KB payloads
|
|
378
|
+
|
|
146
379
|
---
|
|
147
380
|
|
|
148
|
-
##
|
|
381
|
+
## 10. Strictness
|
|
149
382
|
|
|
150
383
|
- Agent surfaces SHOULD default `strict=true`.
|
|
151
384
|
- Strict mode violations SHOULD fail with contract/validation error codes.
|
|
@@ -153,7 +386,7 @@ Rules:
|
|
|
153
386
|
|
|
154
387
|
---
|
|
155
388
|
|
|
156
|
-
##
|
|
389
|
+
## 11. Versioning and Deprecation
|
|
157
390
|
|
|
158
391
|
- Protocol versions MUST follow SemVer.
|
|
159
392
|
- Minor/patch changes MUST be backward compatible.
|
|
@@ -164,6 +397,81 @@ See `docs/VERSIONING.md` and `docs/DEPRECATION.md`.
|
|
|
164
397
|
|
|
165
398
|
---
|
|
166
399
|
|
|
167
|
-
##
|
|
400
|
+
## 12. Conformance
|
|
168
401
|
|
|
169
402
|
Conforming implementations MUST pass minimum checks in `docs/CONFORMANCE.md` and schema validation for the canonical envelope.
|
|
403
|
+
|
|
404
|
+
### 12.1 Adoption Tiers
|
|
405
|
+
|
|
406
|
+
LAFS defines three adoption tiers to enable gradual conformance. Each tier builds on the previous tier's requirements. Implementations MUST declare which tier they target and MUST pass all checks required by that tier.
|
|
407
|
+
|
|
408
|
+
#### 12.1.1 Core Tier
|
|
409
|
+
|
|
410
|
+
The Core tier represents **minimum viable LAFS adoption**. It verifies that responses use the canonical envelope shape and satisfy basic structural invariants.
|
|
411
|
+
|
|
412
|
+
Required conformance checks:
|
|
413
|
+
|
|
414
|
+
| Check | Description |
|
|
415
|
+
|---|---|
|
|
416
|
+
| `envelope_schema_valid` | Response validates against `schemas/v1/envelope.schema.json` |
|
|
417
|
+
| `envelope_invariants` | `success`/`result`/`error` mutual exclusivity holds (Section 6.1) |
|
|
418
|
+
|
|
419
|
+
Use cases: quick adoption, internal APIs, prototyping, evaluating LAFS fit.
|
|
420
|
+
|
|
421
|
+
#### 12.1.2 Standard Tier
|
|
422
|
+
|
|
423
|
+
The Standard tier is **recommended for production** use. It adds semantic checks for error codes, metadata flags, and format defaults on top of all Core tier requirements.
|
|
424
|
+
|
|
425
|
+
Required conformance checks — all Core checks, plus:
|
|
426
|
+
|
|
427
|
+
| Check | Description |
|
|
428
|
+
|---|---|
|
|
429
|
+
| `error_code_registered` | All error codes come from the registered error registry (Section 7) |
|
|
430
|
+
| `meta_mvi_present` | `_meta.mvi` flag is present and valid (Section 9.1) |
|
|
431
|
+
| `meta_strict_present` | `_meta.strict` flag is present and boolean (Section 10) |
|
|
432
|
+
| `json_protocol_default` | JSON is the default output format when no explicit format is requested (Section 5.1) |
|
|
433
|
+
|
|
434
|
+
Use cases: production APIs, public-facing services, third-party integrations.
|
|
435
|
+
|
|
436
|
+
#### 12.1.3 Complete Tier
|
|
437
|
+
|
|
438
|
+
The Complete tier represents **full LAFS compliance**. It adds configuration, flag-handling, and advanced feature checks on top of all Standard tier requirements.
|
|
439
|
+
|
|
440
|
+
Required conformance checks — all Standard checks, plus:
|
|
441
|
+
|
|
442
|
+
| Check | Description |
|
|
443
|
+
|---|---|
|
|
444
|
+
| `config_override_respected` | Project/user config-based format overrides are correctly applied (Section 5.2) |
|
|
445
|
+
| `flag_conflict_rejected` | Conflicting format flags (e.g., `--human --json`) are properly rejected with `E_FORMAT_CONFLICT` (Section 5.1) |
|
|
446
|
+
| `context_validation` | Context preservation invariants hold for multi-step operations (Section 8) |
|
|
447
|
+
| `pagination_validation` | Pagination metadata validates when present (Section 9.3) |
|
|
448
|
+
|
|
449
|
+
Use cases: official LAFS-conformant implementations, reference implementations, certification.
|
|
450
|
+
|
|
451
|
+
> **Note:** `context_validation` and `pagination_validation` are reserved check names. Implementations SHOULD treat these as automatically passing until the corresponding conformance runners are available.
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## 13. Security Considerations
|
|
456
|
+
|
|
457
|
+
This section addresses security threats relevant to LAFS envelope production and consumption. LAFS is transport-agnostic and does not define its own cryptographic or authentication mechanisms; implementers MUST rely on the underlying transport and application layers for those controls.
|
|
458
|
+
|
|
459
|
+
### 13.1 Injection attacks
|
|
460
|
+
|
|
461
|
+
LAFS envelopes carry user-provided data in `result`, `error`, and `details` fields. Implementers MUST sanitize all envelope contents before rendering in HTML, constructing shell commands, or executing in eval-like contexts. Error messages MUST NOT contain unsanitized user input. Implementations that embed envelope values in SQL, LDAP, or similar query languages MUST use parameterized interfaces.
|
|
462
|
+
|
|
463
|
+
### 13.2 Tampering
|
|
464
|
+
|
|
465
|
+
LAFS does not define integrity protection at the envelope level. If envelope integrity is required, implementers SHOULD use transport-level security (e.g., TLS) and MAY implement envelope signing as an extension. Consumers MUST NOT trust envelope contents without verifying the transport channel. Implementations that relay envelopes across trust boundaries SHOULD re-validate against `schemas/v1/envelope.schema.json` at each boundary.
|
|
466
|
+
|
|
467
|
+
### 13.3 Information disclosure
|
|
468
|
+
|
|
469
|
+
Error details MAY contain sensitive information such as stack traces, internal paths, or database identifiers. Implementations SHOULD distinguish between development and production error detail levels. The `details` field in error objects MUST NOT expose internal system information in production environments. Implementations SHOULD define an explicit allow-list of fields permitted in production error responses.
|
|
470
|
+
|
|
471
|
+
### 13.4 Replay attacks
|
|
472
|
+
|
|
473
|
+
LAFS includes `requestId` and `timestamp` in `_meta` for correlation (Section 6). Implementers MAY use these fields for replay detection but MUST NOT rely solely on them, as LAFS does not mandate uniqueness or freshness guarantees for these values. Transport-level replay protection (e.g., TLS with appropriate session management) is RECOMMENDED.
|
|
474
|
+
|
|
475
|
+
### 13.5 Denial of service
|
|
476
|
+
|
|
477
|
+
Large envelope payloads could be used for resource exhaustion. Implementations SHOULD enforce maximum envelope size limits appropriate to their deployment context. Pagination (Section 9.3) SHOULD be used to bound response sizes for list operations. Implementations SHOULD reject envelopes that exceed the configured size limit with a structured error.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleocode/lafs-protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "LLM-Agent-First Specification schemas and conformance tooling",
|
|
@@ -50,13 +50,18 @@
|
|
|
50
50
|
],
|
|
51
51
|
"license": "MIT",
|
|
52
52
|
"devDependencies": {
|
|
53
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
54
|
+
"@types/express": "^5.0.6",
|
|
53
55
|
"@types/node": "^24.3.0",
|
|
56
|
+
"@types/supertest": "^6.0.3",
|
|
57
|
+
"supertest": "^7.2.2",
|
|
54
58
|
"tsx": "^4.20.5",
|
|
55
59
|
"typescript": "^5.9.2",
|
|
56
60
|
"vitest": "^2.1.9"
|
|
57
61
|
},
|
|
58
62
|
"dependencies": {
|
|
59
|
-
"ajv": "^8.
|
|
60
|
-
"ajv-formats": "^3.0.1"
|
|
63
|
+
"ajv": "^8.18.0",
|
|
64
|
+
"ajv-formats": "^3.0.1",
|
|
65
|
+
"express": "^5.2.1"
|
|
61
66
|
}
|
|
62
67
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://lafs.dev/schemas/v1/context-ledger.schema.json",
|
|
4
|
+
"title": "LAFS Context Ledger",
|
|
5
|
+
"description": "Tracks state across request/response cycles for context preservation",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["ledgerId", "version", "createdAt", "updatedAt", "entries", "checksum", "maxEntries"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"ledgerId": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Unique identifier for this context ledger instance"
|
|
12
|
+
},
|
|
13
|
+
"version": {
|
|
14
|
+
"type": "integer",
|
|
15
|
+
"minimum": 0,
|
|
16
|
+
"description": "Monotonically increasing version number matching _meta.contextVersion"
|
|
17
|
+
},
|
|
18
|
+
"createdAt": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"format": "date-time",
|
|
21
|
+
"description": "ISO 8601 timestamp when the ledger was created"
|
|
22
|
+
},
|
|
23
|
+
"updatedAt": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"format": "date-time",
|
|
26
|
+
"description": "ISO 8601 timestamp of the last ledger update"
|
|
27
|
+
},
|
|
28
|
+
"entries": {
|
|
29
|
+
"type": "array",
|
|
30
|
+
"items": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"required": ["entryId", "timestamp", "operation", "contextDelta"],
|
|
33
|
+
"properties": {
|
|
34
|
+
"entryId": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"description": "Unique identifier for this ledger entry"
|
|
37
|
+
},
|
|
38
|
+
"timestamp": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"format": "date-time"
|
|
41
|
+
},
|
|
42
|
+
"operation": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "The operation that generated this entry"
|
|
45
|
+
},
|
|
46
|
+
"contextDelta": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"description": "The context changes introduced by this operation",
|
|
49
|
+
"additionalProperties": true
|
|
50
|
+
},
|
|
51
|
+
"requestId": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"description": "Correlation with the request that produced this entry"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"description": "Ordered array of context entries (append-only)"
|
|
58
|
+
},
|
|
59
|
+
"checksum": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"description": "Integrity checksum of the ledger contents"
|
|
62
|
+
},
|
|
63
|
+
"maxEntries": {
|
|
64
|
+
"type": "integer",
|
|
65
|
+
"minimum": 1,
|
|
66
|
+
"description": "Maximum number of entries before truncation/compaction"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"additionalProperties": false
|
|
70
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://lafs.dev/schemas/v1/discovery.schema.json",
|
|
4
|
+
"title": "LAFS Discovery Document",
|
|
5
|
+
"description": "Schema for LAFS agent discovery documents served at /.well-known/lafs.json",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"$schema",
|
|
9
|
+
"lafs_version",
|
|
10
|
+
"service",
|
|
11
|
+
"capabilities",
|
|
12
|
+
"endpoints"
|
|
13
|
+
],
|
|
14
|
+
"properties": {
|
|
15
|
+
"$schema": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"format": "uri",
|
|
18
|
+
"description": "URL of the schema this document conforms to"
|
|
19
|
+
},
|
|
20
|
+
"lafs_version": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$",
|
|
23
|
+
"description": "LAFS protocol version (semantic versioning)"
|
|
24
|
+
},
|
|
25
|
+
"service": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"description": "Service identification and metadata",
|
|
28
|
+
"required": [
|
|
29
|
+
"name",
|
|
30
|
+
"version"
|
|
31
|
+
],
|
|
32
|
+
"properties": {
|
|
33
|
+
"name": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"minLength": 1,
|
|
36
|
+
"maxLength": 100,
|
|
37
|
+
"description": "Unique service name (kebab-case recommended)"
|
|
38
|
+
},
|
|
39
|
+
"version": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$",
|
|
42
|
+
"description": "Service version (semantic versioning)"
|
|
43
|
+
},
|
|
44
|
+
"description": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"maxLength": 500,
|
|
47
|
+
"description": "Human-readable service description"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"additionalProperties": false
|
|
51
|
+
},
|
|
52
|
+
"capabilities": {
|
|
53
|
+
"type": "array",
|
|
54
|
+
"description": "List of LAFS capabilities this service provides",
|
|
55
|
+
"minItems": 1,
|
|
56
|
+
"items": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"required": [
|
|
59
|
+
"name",
|
|
60
|
+
"version",
|
|
61
|
+
"operations"
|
|
62
|
+
],
|
|
63
|
+
"properties": {
|
|
64
|
+
"name": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"minLength": 1,
|
|
67
|
+
"maxLength": 100,
|
|
68
|
+
"description": "Capability identifier (kebab-case recommended)"
|
|
69
|
+
},
|
|
70
|
+
"version": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$",
|
|
73
|
+
"description": "Capability version (semantic versioning)"
|
|
74
|
+
},
|
|
75
|
+
"description": {
|
|
76
|
+
"type": "string",
|
|
77
|
+
"maxLength": 500,
|
|
78
|
+
"description": "Human-readable capability description"
|
|
79
|
+
},
|
|
80
|
+
"operations": {
|
|
81
|
+
"type": "array",
|
|
82
|
+
"description": "List of operations this capability supports",
|
|
83
|
+
"minItems": 1,
|
|
84
|
+
"items": {
|
|
85
|
+
"type": "string",
|
|
86
|
+
"minLength": 1,
|
|
87
|
+
"maxLength": 50
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"optional": {
|
|
91
|
+
"type": "boolean",
|
|
92
|
+
"default": false,
|
|
93
|
+
"description": "Whether this capability is optional for clients"
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"additionalProperties": false
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"endpoints": {
|
|
100
|
+
"type": "object",
|
|
101
|
+
"description": "URL endpoints for LAFS operations",
|
|
102
|
+
"required": [
|
|
103
|
+
"envelope",
|
|
104
|
+
"discovery"
|
|
105
|
+
],
|
|
106
|
+
"properties": {
|
|
107
|
+
"envelope": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"minLength": 1,
|
|
110
|
+
"description": "URL for envelope submission (POST)"
|
|
111
|
+
},
|
|
112
|
+
"context": {
|
|
113
|
+
"type": "string",
|
|
114
|
+
"minLength": 1,
|
|
115
|
+
"description": "URL for context ledger operations"
|
|
116
|
+
},
|
|
117
|
+
"discovery": {
|
|
118
|
+
"type": "string",
|
|
119
|
+
"minLength": 1,
|
|
120
|
+
"description": "URL of this discovery document"
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"additionalProperties": false
|
|
124
|
+
},
|
|
125
|
+
"extensions": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"description": "Extension fields for vendor-specific metadata",
|
|
128
|
+
"additionalProperties": true
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"additionalProperties": false
|
|
132
|
+
}
|