@cleocode/lafs-protocol 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.
- package/LICENSE +21 -0
- package/README.md +45 -0
- package/dist/schemas/v1/envelope.schema.json +193 -0
- package/dist/schemas/v1/error-registry.json +96 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +49 -0
- package/dist/src/conformance.d.ts +3 -0
- package/dist/src/conformance.js +45 -0
- package/dist/src/errorRegistry.d.ts +15 -0
- package/dist/src/errorRegistry.js +8 -0
- package/dist/src/flagSemantics.d.ts +10 -0
- package/dist/src/flagSemantics.js +29 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +5 -0
- package/dist/src/types.d.ts +52 -0
- package/dist/src/types.js +1 -0
- package/dist/src/validateEnvelope.d.ts +7 -0
- package/dist/src/validateEnvelope.js +28 -0
- package/lafs.md +169 -0
- package/package.json +55 -0
- package/schemas/v1/envelope.schema.json +193 -0
- package/schemas/v1/error-registry.json +96 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# lafs-protocol
|
|
2
|
+
|
|
3
|
+
LLM-Agent-First Specification (LAFS) as a standalone protocol repository.
|
|
4
|
+
|
|
5
|
+
This repository is language-neutral at the protocol layer and TypeScript-first for reference tooling.
|
|
6
|
+
|
|
7
|
+
## What this repo provides
|
|
8
|
+
|
|
9
|
+
- Canonical protocol spec: `lafs.md`
|
|
10
|
+
- Versioned JSON schemas: `schemas/v1/`
|
|
11
|
+
- Error registry and transport mappings: `schemas/v1/error-registry.json`
|
|
12
|
+
- TypeScript validation/conformance toolkit: `src/`
|
|
13
|
+
- Automated conformance tests: `tests/`
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Commands
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm run typecheck
|
|
25
|
+
npm test
|
|
26
|
+
npm run conformance -- --envelope fixtures/valid-success-envelope.json --flags fixtures/flags-valid.json
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Canonical policy
|
|
30
|
+
|
|
31
|
+
- JSON default output is REQUIRED.
|
|
32
|
+
- Human-readable output is explicit opt-in (`--human`).
|
|
33
|
+
- `--json` is optional but recommended explicit alias/override.
|
|
34
|
+
- `--human --json` is invalid and MUST fail with `E_FORMAT_CONFLICT`.
|
|
35
|
+
|
|
36
|
+
## Layout
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
lafs.md
|
|
40
|
+
schemas/v1/
|
|
41
|
+
src/
|
|
42
|
+
tests/
|
|
43
|
+
fixtures/
|
|
44
|
+
docs/
|
|
45
|
+
```
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://lafs.dev/schemas/v1/envelope.schema.json",
|
|
4
|
+
"title": "LAFS Envelope v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"$schema",
|
|
9
|
+
"_meta",
|
|
10
|
+
"success",
|
|
11
|
+
"result",
|
|
12
|
+
"error",
|
|
13
|
+
"page"
|
|
14
|
+
],
|
|
15
|
+
"properties": {
|
|
16
|
+
"$schema": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"const": "https://lafs.dev/schemas/v1/envelope.schema.json"
|
|
19
|
+
},
|
|
20
|
+
"_meta": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"additionalProperties": false,
|
|
23
|
+
"required": [
|
|
24
|
+
"specVersion",
|
|
25
|
+
"schemaVersion",
|
|
26
|
+
"timestamp",
|
|
27
|
+
"operation",
|
|
28
|
+
"requestId",
|
|
29
|
+
"transport",
|
|
30
|
+
"strict",
|
|
31
|
+
"mvi",
|
|
32
|
+
"contextVersion"
|
|
33
|
+
],
|
|
34
|
+
"properties": {
|
|
35
|
+
"specVersion": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
|
38
|
+
},
|
|
39
|
+
"schemaVersion": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
|
42
|
+
},
|
|
43
|
+
"timestamp": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"format": "date-time"
|
|
46
|
+
},
|
|
47
|
+
"operation": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"minLength": 1,
|
|
50
|
+
"maxLength": 128
|
|
51
|
+
},
|
|
52
|
+
"requestId": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"minLength": 3,
|
|
55
|
+
"maxLength": 128
|
|
56
|
+
},
|
|
57
|
+
"transport": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"enum": ["cli", "http", "grpc", "sdk"]
|
|
60
|
+
},
|
|
61
|
+
"strict": {
|
|
62
|
+
"type": "boolean"
|
|
63
|
+
},
|
|
64
|
+
"mvi": {
|
|
65
|
+
"type": "boolean"
|
|
66
|
+
},
|
|
67
|
+
"contextVersion": {
|
|
68
|
+
"type": "integer",
|
|
69
|
+
"minimum": 0
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"success": {
|
|
74
|
+
"type": "boolean"
|
|
75
|
+
},
|
|
76
|
+
"result": {
|
|
77
|
+
"type": ["object", "array", "null"]
|
|
78
|
+
},
|
|
79
|
+
"error": {
|
|
80
|
+
"type": ["object", "null"],
|
|
81
|
+
"additionalProperties": false,
|
|
82
|
+
"required": [
|
|
83
|
+
"code",
|
|
84
|
+
"message",
|
|
85
|
+
"category",
|
|
86
|
+
"retryable",
|
|
87
|
+
"retryAfterMs",
|
|
88
|
+
"details"
|
|
89
|
+
],
|
|
90
|
+
"properties": {
|
|
91
|
+
"code": {
|
|
92
|
+
"type": "string",
|
|
93
|
+
"pattern": "^E_[A-Z0-9]+_[A-Z0-9_]+$"
|
|
94
|
+
},
|
|
95
|
+
"message": {
|
|
96
|
+
"type": "string",
|
|
97
|
+
"minLength": 1,
|
|
98
|
+
"maxLength": 1024
|
|
99
|
+
},
|
|
100
|
+
"category": {
|
|
101
|
+
"type": "string",
|
|
102
|
+
"enum": [
|
|
103
|
+
"VALIDATION",
|
|
104
|
+
"AUTH",
|
|
105
|
+
"PERMISSION",
|
|
106
|
+
"NOT_FOUND",
|
|
107
|
+
"CONFLICT",
|
|
108
|
+
"RATE_LIMIT",
|
|
109
|
+
"TRANSIENT",
|
|
110
|
+
"INTERNAL",
|
|
111
|
+
"CONTRACT",
|
|
112
|
+
"MIGRATION"
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
"retryable": {
|
|
116
|
+
"type": "boolean"
|
|
117
|
+
},
|
|
118
|
+
"retryAfterMs": {
|
|
119
|
+
"type": ["integer", "null"],
|
|
120
|
+
"minimum": 0
|
|
121
|
+
},
|
|
122
|
+
"details": {
|
|
123
|
+
"type": "object"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"page": {
|
|
128
|
+
"type": ["object", "null"],
|
|
129
|
+
"additionalProperties": false,
|
|
130
|
+
"required": [
|
|
131
|
+
"mode",
|
|
132
|
+
"limit",
|
|
133
|
+
"offset",
|
|
134
|
+
"nextCursor",
|
|
135
|
+
"hasMore",
|
|
136
|
+
"total"
|
|
137
|
+
],
|
|
138
|
+
"properties": {
|
|
139
|
+
"mode": {
|
|
140
|
+
"type": "string",
|
|
141
|
+
"enum": ["offset", "cursor", "none"]
|
|
142
|
+
},
|
|
143
|
+
"limit": {
|
|
144
|
+
"type": "integer",
|
|
145
|
+
"minimum": 1,
|
|
146
|
+
"maximum": 1000
|
|
147
|
+
},
|
|
148
|
+
"offset": {
|
|
149
|
+
"type": "integer",
|
|
150
|
+
"minimum": 0
|
|
151
|
+
},
|
|
152
|
+
"nextCursor": {
|
|
153
|
+
"type": ["string", "null"],
|
|
154
|
+
"maxLength": 2048
|
|
155
|
+
},
|
|
156
|
+
"hasMore": {
|
|
157
|
+
"type": "boolean"
|
|
158
|
+
},
|
|
159
|
+
"total": {
|
|
160
|
+
"type": ["integer", "null"],
|
|
161
|
+
"minimum": 0
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"allOf": [
|
|
167
|
+
{
|
|
168
|
+
"if": {
|
|
169
|
+
"properties": {
|
|
170
|
+
"success": { "const": true }
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
"then": {
|
|
174
|
+
"properties": {
|
|
175
|
+
"error": { "type": "null" }
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"if": {
|
|
181
|
+
"properties": {
|
|
182
|
+
"success": { "const": false }
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
"then": {
|
|
186
|
+
"properties": {
|
|
187
|
+
"result": { "type": "null" },
|
|
188
|
+
"error": { "type": "object" }
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"codes": [
|
|
5
|
+
{
|
|
6
|
+
"code": "E_FORMAT_CONFLICT",
|
|
7
|
+
"category": "CONTRACT",
|
|
8
|
+
"description": "Mutually exclusive format flags requested",
|
|
9
|
+
"retryable": false,
|
|
10
|
+
"httpStatus": 400,
|
|
11
|
+
"grpcStatus": "INVALID_ARGUMENT",
|
|
12
|
+
"cliExit": 2
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"code": "E_VALIDATION_SCHEMA",
|
|
16
|
+
"category": "VALIDATION",
|
|
17
|
+
"description": "Input failed schema validation",
|
|
18
|
+
"retryable": false,
|
|
19
|
+
"httpStatus": 400,
|
|
20
|
+
"grpcStatus": "INVALID_ARGUMENT",
|
|
21
|
+
"cliExit": 2
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"code": "E_NOT_FOUND_RESOURCE",
|
|
25
|
+
"category": "NOT_FOUND",
|
|
26
|
+
"description": "Referenced resource was not found",
|
|
27
|
+
"retryable": false,
|
|
28
|
+
"httpStatus": 404,
|
|
29
|
+
"grpcStatus": "NOT_FOUND",
|
|
30
|
+
"cliExit": 4
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"code": "E_CONFLICT_VERSION",
|
|
34
|
+
"category": "CONFLICT",
|
|
35
|
+
"description": "Version or concurrency conflict",
|
|
36
|
+
"retryable": true,
|
|
37
|
+
"httpStatus": 409,
|
|
38
|
+
"grpcStatus": "ABORTED",
|
|
39
|
+
"cliExit": 7
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"code": "E_RATE_LIMITED",
|
|
43
|
+
"category": "RATE_LIMIT",
|
|
44
|
+
"description": "Rate limit exceeded",
|
|
45
|
+
"retryable": true,
|
|
46
|
+
"httpStatus": 429,
|
|
47
|
+
"grpcStatus": "RESOURCE_EXHAUSTED",
|
|
48
|
+
"cliExit": 8
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"code": "E_TRANSIENT_UPSTREAM",
|
|
52
|
+
"category": "TRANSIENT",
|
|
53
|
+
"description": "Upstream dependency temporary failure",
|
|
54
|
+
"retryable": true,
|
|
55
|
+
"httpStatus": 503,
|
|
56
|
+
"grpcStatus": "UNAVAILABLE",
|
|
57
|
+
"cliExit": 9
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"code": "E_INTERNAL_UNEXPECTED",
|
|
61
|
+
"category": "INTERNAL",
|
|
62
|
+
"description": "Unexpected internal failure",
|
|
63
|
+
"retryable": false,
|
|
64
|
+
"httpStatus": 500,
|
|
65
|
+
"grpcStatus": "INTERNAL",
|
|
66
|
+
"cliExit": 1
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"code": "E_CONTEXT_MISSING",
|
|
70
|
+
"category": "CONTRACT",
|
|
71
|
+
"description": "Required context ledger fields are missing",
|
|
72
|
+
"retryable": false,
|
|
73
|
+
"httpStatus": 400,
|
|
74
|
+
"grpcStatus": "FAILED_PRECONDITION",
|
|
75
|
+
"cliExit": 6
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"code": "E_CONTEXT_STALE",
|
|
79
|
+
"category": "CONFLICT",
|
|
80
|
+
"description": "Context ledger or references are stale",
|
|
81
|
+
"retryable": true,
|
|
82
|
+
"httpStatus": 409,
|
|
83
|
+
"grpcStatus": "ABORTED",
|
|
84
|
+
"cliExit": 7
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"code": "E_MIGRATION_UNSUPPORTED_VERSION",
|
|
88
|
+
"category": "MIGRATION",
|
|
89
|
+
"description": "Requested protocol/schema version unsupported",
|
|
90
|
+
"retryable": false,
|
|
91
|
+
"httpStatus": 426,
|
|
92
|
+
"grpcStatus": "FAILED_PRECONDITION",
|
|
93
|
+
"cliExit": 10
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
}
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { runEnvelopeConformance, runFlagConformance } from "./conformance.js";
|
|
4
|
+
function parseArgs(argv) {
|
|
5
|
+
const args = {};
|
|
6
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
7
|
+
const current = argv[i];
|
|
8
|
+
const next = argv[i + 1];
|
|
9
|
+
if (current === "--envelope" && next) {
|
|
10
|
+
args.envelopePath = next;
|
|
11
|
+
i += 1;
|
|
12
|
+
}
|
|
13
|
+
else if (current === "--flags" && next) {
|
|
14
|
+
args.flagsPath = next;
|
|
15
|
+
i += 1;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return args;
|
|
19
|
+
}
|
|
20
|
+
async function readJson(path) {
|
|
21
|
+
const content = await readFile(path, "utf8");
|
|
22
|
+
return JSON.parse(content);
|
|
23
|
+
}
|
|
24
|
+
async function main() {
|
|
25
|
+
const args = parseArgs(process.argv.slice(2));
|
|
26
|
+
const reports = [];
|
|
27
|
+
if (args.envelopePath) {
|
|
28
|
+
const envelope = await readJson(args.envelopePath);
|
|
29
|
+
reports.push({ name: "envelope", report: runEnvelopeConformance(envelope) });
|
|
30
|
+
}
|
|
31
|
+
if (args.flagsPath) {
|
|
32
|
+
const flags = await readJson(args.flagsPath);
|
|
33
|
+
reports.push({ name: "flags", report: runFlagConformance(flags) });
|
|
34
|
+
}
|
|
35
|
+
if (reports.length === 0) {
|
|
36
|
+
throw new Error("Provide --envelope and/or --flags JSON files.");
|
|
37
|
+
}
|
|
38
|
+
console.log(JSON.stringify({ success: true, reports }, null, 2));
|
|
39
|
+
}
|
|
40
|
+
main().catch((error) => {
|
|
41
|
+
console.error(JSON.stringify({
|
|
42
|
+
success: false,
|
|
43
|
+
error: {
|
|
44
|
+
code: "E_INTERNAL_UNEXPECTED",
|
|
45
|
+
message: error instanceof Error ? error.message : String(error)
|
|
46
|
+
}
|
|
47
|
+
}, null, 2));
|
|
48
|
+
process.exit(1);
|
|
49
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { isRegisteredErrorCode } from "./errorRegistry.js";
|
|
2
|
+
import { resolveOutputFormat, LAFSFlagError } from "./flagSemantics.js";
|
|
3
|
+
import { validateEnvelope } from "./validateEnvelope.js";
|
|
4
|
+
function pushCheck(checks, name, pass, detail) {
|
|
5
|
+
checks.push({ name, pass, ...(detail ? { detail } : {}) });
|
|
6
|
+
}
|
|
7
|
+
export function runEnvelopeConformance(envelope) {
|
|
8
|
+
const checks = [];
|
|
9
|
+
const validation = validateEnvelope(envelope);
|
|
10
|
+
pushCheck(checks, "envelope_schema_valid", validation.valid, validation.valid ? undefined : validation.errors.join("; "));
|
|
11
|
+
if (!validation.valid) {
|
|
12
|
+
return { ok: false, checks };
|
|
13
|
+
}
|
|
14
|
+
const typed = envelope;
|
|
15
|
+
const invariant = typed.success ? typed.error === null : typed.result === null;
|
|
16
|
+
pushCheck(checks, "envelope_invariants", invariant, invariant ? undefined : "success/result/error invariant violated");
|
|
17
|
+
if (typed.error) {
|
|
18
|
+
const registered = isRegisteredErrorCode(typed.error.code);
|
|
19
|
+
pushCheck(checks, "error_code_registered", registered, registered ? undefined : `unregistered code: ${typed.error.code}`);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
pushCheck(checks, "error_code_registered", true);
|
|
23
|
+
}
|
|
24
|
+
pushCheck(checks, "meta_mvi_present", typeof typed._meta.mvi === "boolean");
|
|
25
|
+
pushCheck(checks, "meta_strict_present", typeof typed._meta.strict === "boolean");
|
|
26
|
+
return { ok: checks.every((check) => check.pass), checks };
|
|
27
|
+
}
|
|
28
|
+
export function runFlagConformance(flags) {
|
|
29
|
+
const checks = [];
|
|
30
|
+
try {
|
|
31
|
+
const resolved = resolveOutputFormat(flags);
|
|
32
|
+
pushCheck(checks, "flag_conflict_rejected", !(flags.humanFlag && flags.jsonFlag));
|
|
33
|
+
pushCheck(checks, "json_default_when_unspecified", resolved.format === "json" || Boolean(flags.projectDefault || flags.userDefault));
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (error instanceof LAFSFlagError && error.code === "E_FORMAT_CONFLICT") {
|
|
37
|
+
pushCheck(checks, "flag_conflict_rejected", true);
|
|
38
|
+
pushCheck(checks, "json_default_when_unspecified", true);
|
|
39
|
+
return { ok: true, checks };
|
|
40
|
+
}
|
|
41
|
+
pushCheck(checks, "flag_resolution", false, error instanceof Error ? error.message : String(error));
|
|
42
|
+
return { ok: false, checks };
|
|
43
|
+
}
|
|
44
|
+
return { ok: checks.every((check) => check.pass), checks };
|
|
45
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface RegistryCode {
|
|
2
|
+
code: string;
|
|
3
|
+
category: string;
|
|
4
|
+
description: string;
|
|
5
|
+
retryable: boolean;
|
|
6
|
+
httpStatus: number;
|
|
7
|
+
grpcStatus: string;
|
|
8
|
+
cliExit: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ErrorRegistry {
|
|
11
|
+
version: string;
|
|
12
|
+
codes: RegistryCode[];
|
|
13
|
+
}
|
|
14
|
+
export declare function getErrorRegistry(): ErrorRegistry;
|
|
15
|
+
export declare function isRegisteredErrorCode(code: string): boolean;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import errorRegistry from "../schemas/v1/error-registry.json" with { type: "json" };
|
|
2
|
+
export function getErrorRegistry() {
|
|
3
|
+
return errorRegistry;
|
|
4
|
+
}
|
|
5
|
+
export function isRegisteredErrorCode(code) {
|
|
6
|
+
const registry = getErrorRegistry();
|
|
7
|
+
return registry.codes.some((item) => item.code === code);
|
|
8
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FlagInput } from "./types.js";
|
|
2
|
+
export interface FlagResolution {
|
|
3
|
+
format: "json" | "human";
|
|
4
|
+
source: "flag" | "project" | "user" | "default";
|
|
5
|
+
}
|
|
6
|
+
export declare class LAFSFlagError extends Error {
|
|
7
|
+
code: string;
|
|
8
|
+
constructor(code: string, message: string);
|
|
9
|
+
}
|
|
10
|
+
export declare function resolveOutputFormat(input: FlagInput): FlagResolution;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export class LAFSFlagError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
constructor(code, message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "LAFSFlagError";
|
|
6
|
+
this.code = code;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function resolveOutputFormat(input) {
|
|
10
|
+
if (input.humanFlag && input.jsonFlag) {
|
|
11
|
+
throw new LAFSFlagError("E_FORMAT_CONFLICT", "Cannot combine --human and --json in the same invocation.");
|
|
12
|
+
}
|
|
13
|
+
if (input.requestedFormat) {
|
|
14
|
+
return { format: input.requestedFormat, source: "flag" };
|
|
15
|
+
}
|
|
16
|
+
if (input.humanFlag) {
|
|
17
|
+
return { format: "human", source: "flag" };
|
|
18
|
+
}
|
|
19
|
+
if (input.jsonFlag) {
|
|
20
|
+
return { format: "json", source: "flag" };
|
|
21
|
+
}
|
|
22
|
+
if (input.projectDefault) {
|
|
23
|
+
return { format: input.projectDefault, source: "project" };
|
|
24
|
+
}
|
|
25
|
+
if (input.userDefault) {
|
|
26
|
+
return { format: input.userDefault, source: "user" };
|
|
27
|
+
}
|
|
28
|
+
return { format: "json", source: "default" };
|
|
29
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export type LAFSTransport = "cli" | "http" | "grpc" | "sdk";
|
|
2
|
+
export type LAFSErrorCategory = "VALIDATION" | "AUTH" | "PERMISSION" | "NOT_FOUND" | "CONFLICT" | "RATE_LIMIT" | "TRANSIENT" | "INTERNAL" | "CONTRACT" | "MIGRATION";
|
|
3
|
+
export interface LAFSMeta {
|
|
4
|
+
specVersion: string;
|
|
5
|
+
schemaVersion: string;
|
|
6
|
+
timestamp: string;
|
|
7
|
+
operation: string;
|
|
8
|
+
requestId: string;
|
|
9
|
+
transport: LAFSTransport;
|
|
10
|
+
strict: boolean;
|
|
11
|
+
mvi: boolean;
|
|
12
|
+
contextVersion: number;
|
|
13
|
+
}
|
|
14
|
+
export interface LAFSError {
|
|
15
|
+
code: string;
|
|
16
|
+
message: string;
|
|
17
|
+
category: LAFSErrorCategory;
|
|
18
|
+
retryable: boolean;
|
|
19
|
+
retryAfterMs: number | null;
|
|
20
|
+
details: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
export interface LAFSPage {
|
|
23
|
+
mode: "offset" | "cursor" | "none";
|
|
24
|
+
limit: number;
|
|
25
|
+
offset: number;
|
|
26
|
+
nextCursor: string | null;
|
|
27
|
+
hasMore: boolean;
|
|
28
|
+
total: number | null;
|
|
29
|
+
}
|
|
30
|
+
export interface LAFSEnvelope {
|
|
31
|
+
$schema: "https://lafs.dev/schemas/v1/envelope.schema.json";
|
|
32
|
+
_meta: LAFSMeta;
|
|
33
|
+
success: boolean;
|
|
34
|
+
result: Record<string, unknown> | Record<string, unknown>[] | null;
|
|
35
|
+
error: LAFSError | null;
|
|
36
|
+
page: LAFSPage | null;
|
|
37
|
+
}
|
|
38
|
+
export interface FlagInput {
|
|
39
|
+
requestedFormat?: "json" | "human";
|
|
40
|
+
jsonFlag?: boolean;
|
|
41
|
+
humanFlag?: boolean;
|
|
42
|
+
projectDefault?: "json" | "human";
|
|
43
|
+
userDefault?: "json" | "human";
|
|
44
|
+
}
|
|
45
|
+
export interface ConformanceReport {
|
|
46
|
+
ok: boolean;
|
|
47
|
+
checks: Array<{
|
|
48
|
+
name: string;
|
|
49
|
+
pass: boolean;
|
|
50
|
+
detail?: string;
|
|
51
|
+
}>;
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { LAFSEnvelope } from "./types.js";
|
|
2
|
+
export interface EnvelopeValidationResult {
|
|
3
|
+
valid: boolean;
|
|
4
|
+
errors: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare function validateEnvelope(input: unknown): EnvelopeValidationResult;
|
|
7
|
+
export declare function assertEnvelope(input: unknown): LAFSEnvelope;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import envelopeSchema from "../schemas/v1/envelope.schema.json" with { type: "json" };
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
4
|
+
const AjvModule = require("ajv");
|
|
5
|
+
const AddFormatsModule = require("ajv-formats");
|
|
6
|
+
const AjvCtor = (typeof AjvModule === "function" ? AjvModule : AjvModule.default);
|
|
7
|
+
const addFormats = (typeof AddFormatsModule === "function" ? AddFormatsModule : AddFormatsModule.default);
|
|
8
|
+
const ajv = new AjvCtor({ allErrors: true, strict: true, allowUnionTypes: true });
|
|
9
|
+
addFormats(ajv);
|
|
10
|
+
const validate = ajv.compile(envelopeSchema);
|
|
11
|
+
export function validateEnvelope(input) {
|
|
12
|
+
const valid = validate(input);
|
|
13
|
+
if (valid) {
|
|
14
|
+
return { valid: true, errors: [] };
|
|
15
|
+
}
|
|
16
|
+
const errors = (validate.errors ?? []).map((error) => {
|
|
17
|
+
const path = error.instancePath || "/";
|
|
18
|
+
return `${path} ${error.message ?? "validation error"}`.trim();
|
|
19
|
+
});
|
|
20
|
+
return { valid: false, errors };
|
|
21
|
+
}
|
|
22
|
+
export function assertEnvelope(input) {
|
|
23
|
+
const result = validateEnvelope(input);
|
|
24
|
+
if (!result.valid) {
|
|
25
|
+
throw new Error(`Invalid LAFS envelope: ${result.errors.join("; ")}`);
|
|
26
|
+
}
|
|
27
|
+
return input;
|
|
28
|
+
}
|
package/lafs.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# LAFS: LLM-Agent-First Specification
|
|
2
|
+
|
|
3
|
+
## 1. Scope
|
|
4
|
+
|
|
5
|
+
LAFS defines a protocol for software systems whose primary consumer is an LLM agent.
|
|
6
|
+
|
|
7
|
+
LAFS is transport-agnostic and language-agnostic. It applies to CLI, SDK, HTTP, gRPC, and orchestrated multi-agent systems.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 2. RFC 2119 Keywords
|
|
12
|
+
|
|
13
|
+
The keywords MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are interpreted per RFC 2119.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 3. Non-Negotiable Protocol Rules
|
|
18
|
+
|
|
19
|
+
1. Output default MUST be machine-readable JSON.
|
|
20
|
+
2. Human-readable mode MUST be explicit opt-in.
|
|
21
|
+
3. Context continuity MUST be preserved across steps.
|
|
22
|
+
4. MVI (Minimal Viable Information) MUST be default response behavior.
|
|
23
|
+
5. Progressive disclosure MUST be used for expanded detail retrieval.
|
|
24
|
+
6. Contracts MUST be deterministic and testable.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 4. Format Semantics
|
|
29
|
+
|
|
30
|
+
### 4.1 Required output semantics
|
|
31
|
+
|
|
32
|
+
- Default format MUST be `json`.
|
|
33
|
+
- `--human` MUST switch output mode to human-readable.
|
|
34
|
+
- `--json` MAY be supported as explicit alias/override and is RECOMMENDED.
|
|
35
|
+
- Providing both `--human` and `--json` MUST fail with `E_FORMAT_CONFLICT`.
|
|
36
|
+
- Explicit flags MUST override env/config defaults.
|
|
37
|
+
|
|
38
|
+
### 4.2 Recommended precedence
|
|
39
|
+
|
|
40
|
+
1. Explicit CLI/API request value
|
|
41
|
+
2. Project config
|
|
42
|
+
3. Global/user config
|
|
43
|
+
4. Protocol default (`json`)
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 5. Canonical Response Envelope
|
|
48
|
+
|
|
49
|
+
All responses MUST conform to `schemas/v1/envelope.schema.json`.
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"$schema": "https://lafs.dev/schemas/v1/envelope.schema.json",
|
|
54
|
+
"_meta": {
|
|
55
|
+
"specVersion": "1.0.0",
|
|
56
|
+
"schemaVersion": "1.0.0",
|
|
57
|
+
"timestamp": "2026-02-11T00:00:00Z",
|
|
58
|
+
"operation": "operation.name",
|
|
59
|
+
"requestId": "req_123",
|
|
60
|
+
"transport": "cli",
|
|
61
|
+
"strict": true,
|
|
62
|
+
"mvi": true,
|
|
63
|
+
"contextVersion": 0
|
|
64
|
+
},
|
|
65
|
+
"success": true,
|
|
66
|
+
"result": {},
|
|
67
|
+
"error": null,
|
|
68
|
+
"page": null
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 5.1 Envelope invariants
|
|
73
|
+
|
|
74
|
+
- Exactly one of `result` or `error` MUST be non-null.
|
|
75
|
+
- `success=true` implies `error=null`.
|
|
76
|
+
- `success=false` implies `result=null`.
|
|
77
|
+
- Unknown fields SHOULD be rejected when strict mode is enabled.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 6. Error Contract
|
|
82
|
+
|
|
83
|
+
Errors MUST conform to envelope `error` shape and use codes from `schemas/v1/error-registry.json`.
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"code": "E_VALIDATION_SCHEMA",
|
|
88
|
+
"message": "Invalid input payload",
|
|
89
|
+
"category": "VALIDATION",
|
|
90
|
+
"retryable": false,
|
|
91
|
+
"retryAfterMs": null,
|
|
92
|
+
"details": {
|
|
93
|
+
"field": "limit"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 6.1 Required behavior
|
|
99
|
+
|
|
100
|
+
- Error codes MUST be stable within major versions.
|
|
101
|
+
- Retry semantics MUST be encoded in `retryable` and `retryAfterMs`.
|
|
102
|
+
- CLI/HTTP/gRPC mappings SHOULD follow the registry.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 7. Context Preservation
|
|
107
|
+
|
|
108
|
+
Multi-step operations MUST preserve a context ledger with at least:
|
|
109
|
+
|
|
110
|
+
- `objective`
|
|
111
|
+
- `constraints[]`
|
|
112
|
+
- `references[]`
|
|
113
|
+
- `decisions[]`
|
|
114
|
+
- `openIssues[]`
|
|
115
|
+
- `state`
|
|
116
|
+
- `version`
|
|
117
|
+
|
|
118
|
+
Rules:
|
|
119
|
+
|
|
120
|
+
- Version MUST increase monotonically by 1 for accepted mutations.
|
|
121
|
+
- Accepted active constraints MUST NOT be silently removed.
|
|
122
|
+
- Decisions affecting output MUST be represented in ledger state.
|
|
123
|
+
- Missing required context for a mutating step MUST fail with structured error.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 8. MVI and Progressive Disclosure
|
|
128
|
+
|
|
129
|
+
### 8.1 MVI default
|
|
130
|
+
|
|
131
|
+
- Default list/batch outputs MUST only contain fields required for next action.
|
|
132
|
+
- Verbose fields SHOULD be omitted by default.
|
|
133
|
+
- Systems SHOULD publish operation-level MVI budgets.
|
|
134
|
+
|
|
135
|
+
### 8.2 Progressive disclosure
|
|
136
|
+
|
|
137
|
+
- Expanded detail retrieval MUST require explicit request.
|
|
138
|
+
- Unknown expansion fields SHOULD fail validation.
|
|
139
|
+
|
|
140
|
+
### 8.3 Pagination
|
|
141
|
+
|
|
142
|
+
- List operations SHOULD return deterministic `page` metadata.
|
|
143
|
+
- Pagination mode (offset or cursor) MUST be documented.
|
|
144
|
+
- Mixed pagination modes in one request MUST fail validation.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 9. Strictness
|
|
149
|
+
|
|
150
|
+
- Agent surfaces SHOULD default `strict=true`.
|
|
151
|
+
- Strict mode violations SHOULD fail with contract/validation error codes.
|
|
152
|
+
- Response metadata MUST expose strict mode status.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 10. Versioning and Deprecation
|
|
157
|
+
|
|
158
|
+
- Protocol versions MUST follow SemVer.
|
|
159
|
+
- Minor/patch changes MUST be backward compatible.
|
|
160
|
+
- Breaking changes MUST require major version increments.
|
|
161
|
+
- Deprecated fields MUST have documented sunset policy.
|
|
162
|
+
|
|
163
|
+
See `docs/VERSIONING.md` and `docs/DEPRECATION.md`.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 11. Conformance
|
|
168
|
+
|
|
169
|
+
Conforming implementations MUST pass minimum checks in `docs/CONFORMANCE.md` and schema validation for the canonical envelope.
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cleocode/lafs-protocol",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "LLM-Agent-First Specification schemas and conformance tooling",
|
|
7
|
+
"main": "dist/src/index.js",
|
|
8
|
+
"types": "dist/src/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"schemas",
|
|
12
|
+
"lafs.md",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/kryptobaseddev/lafs-protocol.git"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/kryptobaseddev/lafs-protocol#readme",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/kryptobaseddev/lafs-protocol/issues"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"bin": {
|
|
28
|
+
"lafs-conformance": "dist/src/cli.js"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"conformance": "tsx src/cli.ts"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"lafs",
|
|
38
|
+
"llm",
|
|
39
|
+
"agent",
|
|
40
|
+
"protocol",
|
|
41
|
+
"schema",
|
|
42
|
+
"conformance"
|
|
43
|
+
],
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^24.3.0",
|
|
47
|
+
"tsx": "^4.20.5",
|
|
48
|
+
"typescript": "^5.9.2",
|
|
49
|
+
"vitest": "^2.1.9"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"ajv": "^8.17.1",
|
|
53
|
+
"ajv-formats": "^3.0.1"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://lafs.dev/schemas/v1/envelope.schema.json",
|
|
4
|
+
"title": "LAFS Envelope v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"$schema",
|
|
9
|
+
"_meta",
|
|
10
|
+
"success",
|
|
11
|
+
"result",
|
|
12
|
+
"error",
|
|
13
|
+
"page"
|
|
14
|
+
],
|
|
15
|
+
"properties": {
|
|
16
|
+
"$schema": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"const": "https://lafs.dev/schemas/v1/envelope.schema.json"
|
|
19
|
+
},
|
|
20
|
+
"_meta": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"additionalProperties": false,
|
|
23
|
+
"required": [
|
|
24
|
+
"specVersion",
|
|
25
|
+
"schemaVersion",
|
|
26
|
+
"timestamp",
|
|
27
|
+
"operation",
|
|
28
|
+
"requestId",
|
|
29
|
+
"transport",
|
|
30
|
+
"strict",
|
|
31
|
+
"mvi",
|
|
32
|
+
"contextVersion"
|
|
33
|
+
],
|
|
34
|
+
"properties": {
|
|
35
|
+
"specVersion": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
|
38
|
+
},
|
|
39
|
+
"schemaVersion": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
|
42
|
+
},
|
|
43
|
+
"timestamp": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"format": "date-time"
|
|
46
|
+
},
|
|
47
|
+
"operation": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"minLength": 1,
|
|
50
|
+
"maxLength": 128
|
|
51
|
+
},
|
|
52
|
+
"requestId": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"minLength": 3,
|
|
55
|
+
"maxLength": 128
|
|
56
|
+
},
|
|
57
|
+
"transport": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"enum": ["cli", "http", "grpc", "sdk"]
|
|
60
|
+
},
|
|
61
|
+
"strict": {
|
|
62
|
+
"type": "boolean"
|
|
63
|
+
},
|
|
64
|
+
"mvi": {
|
|
65
|
+
"type": "boolean"
|
|
66
|
+
},
|
|
67
|
+
"contextVersion": {
|
|
68
|
+
"type": "integer",
|
|
69
|
+
"minimum": 0
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"success": {
|
|
74
|
+
"type": "boolean"
|
|
75
|
+
},
|
|
76
|
+
"result": {
|
|
77
|
+
"type": ["object", "array", "null"]
|
|
78
|
+
},
|
|
79
|
+
"error": {
|
|
80
|
+
"type": ["object", "null"],
|
|
81
|
+
"additionalProperties": false,
|
|
82
|
+
"required": [
|
|
83
|
+
"code",
|
|
84
|
+
"message",
|
|
85
|
+
"category",
|
|
86
|
+
"retryable",
|
|
87
|
+
"retryAfterMs",
|
|
88
|
+
"details"
|
|
89
|
+
],
|
|
90
|
+
"properties": {
|
|
91
|
+
"code": {
|
|
92
|
+
"type": "string",
|
|
93
|
+
"pattern": "^E_[A-Z0-9]+_[A-Z0-9_]+$"
|
|
94
|
+
},
|
|
95
|
+
"message": {
|
|
96
|
+
"type": "string",
|
|
97
|
+
"minLength": 1,
|
|
98
|
+
"maxLength": 1024
|
|
99
|
+
},
|
|
100
|
+
"category": {
|
|
101
|
+
"type": "string",
|
|
102
|
+
"enum": [
|
|
103
|
+
"VALIDATION",
|
|
104
|
+
"AUTH",
|
|
105
|
+
"PERMISSION",
|
|
106
|
+
"NOT_FOUND",
|
|
107
|
+
"CONFLICT",
|
|
108
|
+
"RATE_LIMIT",
|
|
109
|
+
"TRANSIENT",
|
|
110
|
+
"INTERNAL",
|
|
111
|
+
"CONTRACT",
|
|
112
|
+
"MIGRATION"
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
"retryable": {
|
|
116
|
+
"type": "boolean"
|
|
117
|
+
},
|
|
118
|
+
"retryAfterMs": {
|
|
119
|
+
"type": ["integer", "null"],
|
|
120
|
+
"minimum": 0
|
|
121
|
+
},
|
|
122
|
+
"details": {
|
|
123
|
+
"type": "object"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"page": {
|
|
128
|
+
"type": ["object", "null"],
|
|
129
|
+
"additionalProperties": false,
|
|
130
|
+
"required": [
|
|
131
|
+
"mode",
|
|
132
|
+
"limit",
|
|
133
|
+
"offset",
|
|
134
|
+
"nextCursor",
|
|
135
|
+
"hasMore",
|
|
136
|
+
"total"
|
|
137
|
+
],
|
|
138
|
+
"properties": {
|
|
139
|
+
"mode": {
|
|
140
|
+
"type": "string",
|
|
141
|
+
"enum": ["offset", "cursor", "none"]
|
|
142
|
+
},
|
|
143
|
+
"limit": {
|
|
144
|
+
"type": "integer",
|
|
145
|
+
"minimum": 1,
|
|
146
|
+
"maximum": 1000
|
|
147
|
+
},
|
|
148
|
+
"offset": {
|
|
149
|
+
"type": "integer",
|
|
150
|
+
"minimum": 0
|
|
151
|
+
},
|
|
152
|
+
"nextCursor": {
|
|
153
|
+
"type": ["string", "null"],
|
|
154
|
+
"maxLength": 2048
|
|
155
|
+
},
|
|
156
|
+
"hasMore": {
|
|
157
|
+
"type": "boolean"
|
|
158
|
+
},
|
|
159
|
+
"total": {
|
|
160
|
+
"type": ["integer", "null"],
|
|
161
|
+
"minimum": 0
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"allOf": [
|
|
167
|
+
{
|
|
168
|
+
"if": {
|
|
169
|
+
"properties": {
|
|
170
|
+
"success": { "const": true }
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
"then": {
|
|
174
|
+
"properties": {
|
|
175
|
+
"error": { "type": "null" }
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"if": {
|
|
181
|
+
"properties": {
|
|
182
|
+
"success": { "const": false }
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
"then": {
|
|
186
|
+
"properties": {
|
|
187
|
+
"result": { "type": "null" },
|
|
188
|
+
"error": { "type": "object" }
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"codes": [
|
|
5
|
+
{
|
|
6
|
+
"code": "E_FORMAT_CONFLICT",
|
|
7
|
+
"category": "CONTRACT",
|
|
8
|
+
"description": "Mutually exclusive format flags requested",
|
|
9
|
+
"retryable": false,
|
|
10
|
+
"httpStatus": 400,
|
|
11
|
+
"grpcStatus": "INVALID_ARGUMENT",
|
|
12
|
+
"cliExit": 2
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"code": "E_VALIDATION_SCHEMA",
|
|
16
|
+
"category": "VALIDATION",
|
|
17
|
+
"description": "Input failed schema validation",
|
|
18
|
+
"retryable": false,
|
|
19
|
+
"httpStatus": 400,
|
|
20
|
+
"grpcStatus": "INVALID_ARGUMENT",
|
|
21
|
+
"cliExit": 2
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"code": "E_NOT_FOUND_RESOURCE",
|
|
25
|
+
"category": "NOT_FOUND",
|
|
26
|
+
"description": "Referenced resource was not found",
|
|
27
|
+
"retryable": false,
|
|
28
|
+
"httpStatus": 404,
|
|
29
|
+
"grpcStatus": "NOT_FOUND",
|
|
30
|
+
"cliExit": 4
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"code": "E_CONFLICT_VERSION",
|
|
34
|
+
"category": "CONFLICT",
|
|
35
|
+
"description": "Version or concurrency conflict",
|
|
36
|
+
"retryable": true,
|
|
37
|
+
"httpStatus": 409,
|
|
38
|
+
"grpcStatus": "ABORTED",
|
|
39
|
+
"cliExit": 7
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"code": "E_RATE_LIMITED",
|
|
43
|
+
"category": "RATE_LIMIT",
|
|
44
|
+
"description": "Rate limit exceeded",
|
|
45
|
+
"retryable": true,
|
|
46
|
+
"httpStatus": 429,
|
|
47
|
+
"grpcStatus": "RESOURCE_EXHAUSTED",
|
|
48
|
+
"cliExit": 8
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"code": "E_TRANSIENT_UPSTREAM",
|
|
52
|
+
"category": "TRANSIENT",
|
|
53
|
+
"description": "Upstream dependency temporary failure",
|
|
54
|
+
"retryable": true,
|
|
55
|
+
"httpStatus": 503,
|
|
56
|
+
"grpcStatus": "UNAVAILABLE",
|
|
57
|
+
"cliExit": 9
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"code": "E_INTERNAL_UNEXPECTED",
|
|
61
|
+
"category": "INTERNAL",
|
|
62
|
+
"description": "Unexpected internal failure",
|
|
63
|
+
"retryable": false,
|
|
64
|
+
"httpStatus": 500,
|
|
65
|
+
"grpcStatus": "INTERNAL",
|
|
66
|
+
"cliExit": 1
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"code": "E_CONTEXT_MISSING",
|
|
70
|
+
"category": "CONTRACT",
|
|
71
|
+
"description": "Required context ledger fields are missing",
|
|
72
|
+
"retryable": false,
|
|
73
|
+
"httpStatus": 400,
|
|
74
|
+
"grpcStatus": "FAILED_PRECONDITION",
|
|
75
|
+
"cliExit": 6
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"code": "E_CONTEXT_STALE",
|
|
79
|
+
"category": "CONFLICT",
|
|
80
|
+
"description": "Context ledger or references are stale",
|
|
81
|
+
"retryable": true,
|
|
82
|
+
"httpStatus": 409,
|
|
83
|
+
"grpcStatus": "ABORTED",
|
|
84
|
+
"cliExit": 7
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"code": "E_MIGRATION_UNSUPPORTED_VERSION",
|
|
88
|
+
"category": "MIGRATION",
|
|
89
|
+
"description": "Requested protocol/schema version unsupported",
|
|
90
|
+
"retryable": false,
|
|
91
|
+
"httpStatus": 426,
|
|
92
|
+
"grpcStatus": "FAILED_PRECONDITION",
|
|
93
|
+
"cliExit": 10
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
}
|