@curatelabs/graphforge-agent-skills 0.5.1

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,41 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://graphforge.dev/schemas/agent-skills/skill-manifest-v1.json",
4
+ "title": "GraphForge agent skill manifest v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schema_version",
9
+ "id",
10
+ "title",
11
+ "description",
12
+ "input_schema",
13
+ "output_schema",
14
+ "required_capabilities"
15
+ ],
16
+ "properties": {
17
+ "schema_version": { "const": 1 },
18
+ "id": {
19
+ "type": "string",
20
+ "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
21
+ "maxLength": 64
22
+ },
23
+ "title": { "type": "string", "minLength": 1, "maxLength": 120 },
24
+ "description": { "type": "string", "minLength": 1, "maxLength": 500 },
25
+ "input_schema": { "const": "graphforge-agent-skill-input/v1" },
26
+ "output_schema": { "const": "graphforge-agent-skill-output/v1" },
27
+ "required_capabilities": {
28
+ "type": "object",
29
+ "maxProperties": 64,
30
+ "propertyNames": {
31
+ "pattern": "^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$",
32
+ "maxLength": 80
33
+ },
34
+ "additionalProperties": {
35
+ "type": "integer",
36
+ "minimum": 1,
37
+ "maximum": 2147483647
38
+ }
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,233 @@
1
+ const MAX_DIAGNOSTICS = 8;
2
+ const MAX_PAYLOAD_DEPTH = 16;
3
+ const MAX_PAYLOAD_ENTRIES = 4096;
4
+ const MAX_PAYLOAD_STRING_LENGTH = 4096;
5
+ const MAX_PAYLOAD_OBJECT_PROPERTIES = 128;
6
+ const MAX_PAYLOAD_ARRAY_ITEMS = 4096;
7
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
8
+ const SKILL_ID = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
9
+ const CAPABILITY_ID = /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/;
10
+ const ERROR_CODE = /^GF_AGENT_[A-Z0-9_]+$/;
11
+
12
+ const manifestFields = [
13
+ "schema_version",
14
+ "id",
15
+ "title",
16
+ "description",
17
+ "input_schema",
18
+ "output_schema",
19
+ "required_capabilities",
20
+ ];
21
+ const inputFields = ["schema_version", "skill_id", "request_id", "input"];
22
+ const outputFields = ["schema_version", "skill_id", "request_id", "status", "output", "error"];
23
+
24
+ export function validateSkillManifest(value) {
25
+ const diagnostics = [];
26
+ validateClosedObject(value, "$", manifestFields, manifestFields, diagnostics);
27
+ if (isObject(value)) {
28
+ constant(value.schema_version, 1, "$.schema_version", diagnostics);
29
+ stringPattern(value.id, SKILL_ID, 1, 64, "$.id", diagnostics);
30
+ boundedString(value.title, 1, 120, "$.title", diagnostics);
31
+ boundedString(value.description, 1, 500, "$.description", diagnostics);
32
+ constant(value.input_schema, "graphforge-agent-skill-input/v1", "$.input_schema", diagnostics);
33
+ constant(
34
+ value.output_schema,
35
+ "graphforge-agent-skill-output/v1",
36
+ "$.output_schema",
37
+ diagnostics,
38
+ );
39
+ validateCapabilities(value.required_capabilities, diagnostics);
40
+ }
41
+ return result(diagnostics);
42
+ }
43
+
44
+ export function validateSkillInput(value) {
45
+ const diagnostics = [];
46
+ validateClosedObject(value, "$", inputFields, inputFields, diagnostics);
47
+ if (isObject(value)) {
48
+ validateEnvelopeIdentity(value, diagnostics);
49
+ payloadObject(value.input, 128, "$.input", diagnostics);
50
+ }
51
+ return result(diagnostics);
52
+ }
53
+
54
+ export function validateSkillOutput(value) {
55
+ const diagnostics = [];
56
+ validateClosedObject(value, "$", outputFields, outputFields.slice(0, 4), diagnostics);
57
+ if (isObject(value)) {
58
+ validateEnvelopeIdentity(value, diagnostics);
59
+ if (value.status !== "ok" && value.status !== "error") {
60
+ add(diagnostics, "invalid_value", "$.status", "must be one of the supported values");
61
+ } else if (value.status === "ok") {
62
+ required(value, "output", "$", diagnostics);
63
+ forbidden(value, "error", "$", diagnostics);
64
+ if (Object.hasOwn(value, "output")) payloadObject(value.output, 128, "$.output", diagnostics);
65
+ } else {
66
+ required(value, "error", "$", diagnostics);
67
+ forbidden(value, "output", "$", diagnostics);
68
+ if (Object.hasOwn(value, "error")) validateError(value.error, diagnostics);
69
+ }
70
+ }
71
+ return result(diagnostics);
72
+ }
73
+
74
+ function validateEnvelopeIdentity(value, diagnostics) {
75
+ constant(value.schema_version, 1, "$.schema_version", diagnostics);
76
+ stringPattern(value.skill_id, SKILL_ID, 1, 64, "$.skill_id", diagnostics);
77
+ stringPattern(value.request_id, UUID, 36, 36, "$.request_id", diagnostics);
78
+ }
79
+
80
+ function validateCapabilities(value, diagnostics) {
81
+ if (!objectWithLimit(value, 64, "$.required_capabilities", diagnostics)) return;
82
+ for (const [name, version] of Object.entries(value)) {
83
+ const path = CAPABILITY_ID.test(name)
84
+ ? `$.required_capabilities.${name}`
85
+ : "$.required_capabilities.<field>";
86
+ if (!CAPABILITY_ID.test(name) || name.length > 80) {
87
+ add(diagnostics, "invalid_field", path, "field name does not match the schema");
88
+ }
89
+ if (!Number.isSafeInteger(version) || version < 1 || version > 2147483647) {
90
+ add(diagnostics, "invalid_value", path, "must be a supported positive integer version");
91
+ }
92
+ }
93
+ }
94
+
95
+ function validateError(value, diagnostics) {
96
+ const fields = ["code", "message", "details"];
97
+ if (!validateClosedObject(value, "$.error", fields, ["code", "message"], diagnostics)) return;
98
+ stringPattern(value.code, ERROR_CODE, 1, 80, "$.error.code", diagnostics);
99
+ boundedString(value.message, 1, 500, "$.error.message", diagnostics);
100
+ if (Object.hasOwn(value, "details"))
101
+ payloadObject(value.details, 64, "$.error.details", diagnostics);
102
+ }
103
+
104
+ function payloadObject(value, maximum, path, diagnostics) {
105
+ if (!objectWithLimit(value, maximum, path, diagnostics)) return false;
106
+ const seen = new WeakSet();
107
+ let entries = 0;
108
+ let budgetReported = false;
109
+
110
+ function visit(item, depth) {
111
+ entries += 1;
112
+ if (depth > MAX_PAYLOAD_DEPTH || entries > MAX_PAYLOAD_ENTRIES) {
113
+ if (!budgetReported) {
114
+ add(diagnostics, "invalid_size", path, "payload exceeds the recursive schema budget");
115
+ budgetReported = true;
116
+ }
117
+ return;
118
+ }
119
+ if (typeof item === "string" && item.length > MAX_PAYLOAD_STRING_LENGTH) {
120
+ if (!budgetReported) {
121
+ add(diagnostics, "invalid_size", path, "payload exceeds the recursive schema budget");
122
+ budgetReported = true;
123
+ }
124
+ return;
125
+ }
126
+ if (!item || typeof item !== "object") return;
127
+ if (seen.has(item)) {
128
+ if (!budgetReported) {
129
+ add(diagnostics, "invalid_value", path, "payload must not contain cycles");
130
+ budgetReported = true;
131
+ }
132
+ return;
133
+ }
134
+ const nodeSize = Array.isArray(item) ? item.length : Object.keys(item).length;
135
+ const nodeLimit = Array.isArray(item) ? MAX_PAYLOAD_ARRAY_ITEMS : MAX_PAYLOAD_OBJECT_PROPERTIES;
136
+ if (nodeSize > nodeLimit) {
137
+ if (!budgetReported) {
138
+ add(diagnostics, "invalid_size", path, "payload exceeds the recursive schema budget");
139
+ budgetReported = true;
140
+ }
141
+ return;
142
+ }
143
+ seen.add(item);
144
+ for (const [key, child] of Object.entries(item)) {
145
+ visit(key, depth + 1);
146
+ visit(child, depth + 1);
147
+ if (budgetReported) break;
148
+ }
149
+ seen.delete(item);
150
+ }
151
+
152
+ visit(value, 0);
153
+ return !budgetReported;
154
+ }
155
+
156
+ function validateClosedObject(value, path, allowed, requiredFields, diagnostics) {
157
+ if (!isObject(value)) {
158
+ add(diagnostics, "invalid_type", path, "must be an object");
159
+ return false;
160
+ }
161
+ for (const field of requiredFields) required(value, field, path, diagnostics);
162
+ for (const field of Object.keys(value)) {
163
+ if (!allowed.includes(field))
164
+ add(diagnostics, "unknown_field", `${path}.<field>`, "field is not allowed");
165
+ }
166
+ return true;
167
+ }
168
+
169
+ function required(value, field, path, diagnostics) {
170
+ if (!Object.hasOwn(value, field))
171
+ add(diagnostics, "missing_field", `${path}.${field}`, "required field is missing");
172
+ }
173
+
174
+ function forbidden(value, field, path, diagnostics) {
175
+ if (Object.hasOwn(value, field))
176
+ add(
177
+ diagnostics,
178
+ "conflicting_field",
179
+ `${path}.${field}`,
180
+ "field is not allowed for this status",
181
+ );
182
+ }
183
+
184
+ function constant(value, expected, path, diagnostics) {
185
+ if (value !== undefined && value !== expected)
186
+ add(diagnostics, "incompatible_version", path, "value is not supported by this schema version");
187
+ }
188
+
189
+ function boundedString(value, minimum, maximum, path, diagnostics) {
190
+ if (value === undefined) return false;
191
+ if (typeof value !== "string") {
192
+ add(diagnostics, "invalid_type", path, "must be a string");
193
+ return false;
194
+ }
195
+ if (value.length < minimum || value.length > maximum) {
196
+ add(diagnostics, "invalid_length", path, "string length is outside the schema bounds");
197
+ return false;
198
+ }
199
+ return true;
200
+ }
201
+
202
+ function stringPattern(value, pattern, minimum, maximum, path, diagnostics) {
203
+ if (boundedString(value, minimum, maximum, path, diagnostics) && !pattern.test(value)) {
204
+ add(diagnostics, "invalid_value", path, "value does not match the required format");
205
+ }
206
+ }
207
+
208
+ function objectWithLimit(value, maximum, path, diagnostics) {
209
+ if (!isObject(value)) {
210
+ if (value !== undefined) add(diagnostics, "invalid_type", path, "must be an object");
211
+ return false;
212
+ }
213
+ if (Object.keys(value).length > maximum)
214
+ add(diagnostics, "invalid_size", path, "object exceeds the schema field limit");
215
+ return true;
216
+ }
217
+
218
+ function isObject(value) {
219
+ return value !== null && typeof value === "object" && !Array.isArray(value);
220
+ }
221
+
222
+ function add(diagnostics, code, path, message) {
223
+ diagnostics.push({ code, message, path });
224
+ }
225
+
226
+ function result(diagnostics) {
227
+ const stable = diagnostics
228
+ .sort(
229
+ (left, right) => left.path.localeCompare(right.path) || left.code.localeCompare(right.code),
230
+ )
231
+ .slice(0, MAX_DIAGNOSTICS);
232
+ return { diagnostics: stable, valid: stable.length === 0 };
233
+ }
@@ -0,0 +1,10 @@
1
+ # Agent workflows
2
+
3
+ `bootstrap` creates or reopens a local project and verifies a real
4
+ create/reopen/query cycle. `build-knowledge` appends caller-specified graph and
5
+ M20 records, with optional explicit M21 reasoning and first status. Both use the
6
+ shared adapter and injected shipped Node/Arrow surfaces; neither contains a
7
+ runtime, fallback backend, inference, search, traversal, or algorithm behavior.
8
+
9
+ The checked manifest for each workflow is stored beside this file. Import the
10
+ implementations from `@curatelabs/graphforge-agent-skills/workflows`.
@@ -0,0 +1,9 @@
1
+ {
2
+ "schema_version": 1,
3
+ "id": "bootstrap",
4
+ "title": "Bootstrap a local GraphForge project",
5
+ "description": "Create or reopen a local project and verify a real zero-server create, reopen, and query cycle.",
6
+ "input_schema": "graphforge-agent-skill-input/v1",
7
+ "output_schema": "graphforge-agent-skill-output/v1",
8
+ "required_capabilities": { "graph": 1 }
9
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "schema_version": 1,
3
+ "id": "build-knowledge",
4
+ "title": "Build GraphForge graph and knowledge records",
5
+ "description": "Append UUID-backed graph records and required M20 provenance, assertion, confidence, and evidence records, with optional additional M21 status and reasoning, without inference or overwrite.",
6
+ "input_schema": "graphforge-agent-skill-input/v1",
7
+ "output_schema": "graphforge-agent-skill-output/v1",
8
+ "required_capabilities": {
9
+ "graph": 1,
10
+ "knowledge": 1,
11
+ "provenance": 1
12
+ }
13
+ }