@exellix/graph-composer 2.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.
Files changed (103) hide show
  1. package/.env.example +66 -0
  2. package/LICENSE +21 -0
  3. package/README.md +329 -0
  4. package/dist/aiTaskProfile.d.ts +66 -0
  5. package/dist/aiTaskProfile.d.ts.map +1 -0
  6. package/dist/aiTaskProfile.js +179 -0
  7. package/dist/canonicalGraphDocument.d.ts +8 -0
  8. package/dist/canonicalGraphDocument.d.ts.map +1 -0
  9. package/dist/canonicalGraphDocument.js +344 -0
  10. package/dist/canonicalGraphWarnings.d.ts +6 -0
  11. package/dist/canonicalGraphWarnings.d.ts.map +1 -0
  12. package/dist/canonicalGraphWarnings.js +140 -0
  13. package/dist/catalogMatchAssist.d.ts +20 -0
  14. package/dist/catalogMatchAssist.d.ts.map +1 -0
  15. package/dist/catalogMatchAssist.js +203 -0
  16. package/dist/cataloxCatalogBridge.d.ts +103 -0
  17. package/dist/cataloxCatalogBridge.d.ts.map +1 -0
  18. package/dist/cataloxCatalogBridge.js +222 -0
  19. package/dist/cli.d.ts +3 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +43 -0
  22. package/dist/composeInstructions.d.ts +11 -0
  23. package/dist/composeInstructions.d.ts.map +1 -0
  24. package/dist/composeInstructions.js +39 -0
  25. package/dist/defaultUtilitySkills.d.ts +4 -0
  26. package/dist/defaultUtilitySkills.d.ts.map +1 -0
  27. package/dist/defaultUtilitySkills.js +5 -0
  28. package/dist/exampleGeneration.d.ts +15 -0
  29. package/dist/exampleGeneration.d.ts.map +1 -0
  30. package/dist/exampleGeneration.js +72 -0
  31. package/dist/exampleInputs.d.ts +12 -0
  32. package/dist/exampleInputs.d.ts.map +1 -0
  33. package/dist/exampleInputs.js +181 -0
  34. package/dist/graphComposerActions.d.ts +22 -0
  35. package/dist/graphComposerActions.d.ts.map +1 -0
  36. package/dist/graphComposerActions.js +168 -0
  37. package/dist/graphComposerAgent.d.ts +26 -0
  38. package/dist/graphComposerAgent.d.ts.map +1 -0
  39. package/dist/graphComposerAgent.js +175 -0
  40. package/dist/graphComposerOutputValidation.d.ts +23 -0
  41. package/dist/graphComposerOutputValidation.d.ts.map +1 -0
  42. package/dist/graphComposerOutputValidation.js +709 -0
  43. package/dist/graphConceptPatchMerge.d.ts +10 -0
  44. package/dist/graphConceptPatchMerge.d.ts.map +1 -0
  45. package/dist/graphConceptPatchMerge.js +40 -0
  46. package/dist/graphEngineBridge.d.ts +7 -0
  47. package/dist/graphEngineBridge.d.ts.map +1 -0
  48. package/dist/graphEngineBridge.js +5 -0
  49. package/dist/index.d.ts +24 -0
  50. package/dist/index.d.ts.map +1 -0
  51. package/dist/index.js +19 -0
  52. package/dist/openRouterConnectTimeout.d.ts +3 -0
  53. package/dist/openRouterConnectTimeout.d.ts.map +1 -0
  54. package/dist/openRouterConnectTimeout.js +48 -0
  55. package/dist/packDir.d.ts +7 -0
  56. package/dist/packDir.d.ts.map +1 -0
  57. package/dist/packDir.js +23 -0
  58. package/dist/parseGraphConceptStory.d.ts +21 -0
  59. package/dist/parseGraphConceptStory.d.ts.map +1 -0
  60. package/dist/parseGraphConceptStory.js +105 -0
  61. package/dist/redactForLog.d.ts +2 -0
  62. package/dist/redactForLog.d.ts.map +1 -0
  63. package/dist/redactForLog.js +37 -0
  64. package/dist/runGraphComposer.d.ts +54 -0
  65. package/dist/runGraphComposer.d.ts.map +1 -0
  66. package/dist/runGraphComposer.js +444 -0
  67. package/dist/scopingCatalogHostTypes.d.ts +28 -0
  68. package/dist/scopingCatalogHostTypes.d.ts.map +1 -0
  69. package/dist/scopingCatalogHostTypes.js +6 -0
  70. package/dist/scopingNeedMatchAssist.d.ts +14 -0
  71. package/dist/scopingNeedMatchAssist.d.ts.map +1 -0
  72. package/dist/scopingNeedMatchAssist.js +58 -0
  73. package/dist/taskNodeTaskVariable.d.ts +44 -0
  74. package/dist/taskNodeTaskVariable.d.ts.map +1 -0
  75. package/dist/taskNodeTaskVariable.js +347 -0
  76. package/dist/types.d.ts +174 -0
  77. package/dist/types.d.ts.map +1 -0
  78. package/dist/types.js +1 -0
  79. package/examples/network-vuln-subnet-triage.v2.json +389 -0
  80. package/functions/graph-composer/meta.json +607 -0
  81. package/functions/graph-composer/prompts/README.md +46 -0
  82. package/functions/graph-composer/prompts/action-create.md +51 -0
  83. package/functions/graph-composer/prompts/action-explain.md +26 -0
  84. package/functions/graph-composer/prompts/action-modify.md +32 -0
  85. package/functions/graph-composer/prompts/action-review-concept.md +97 -0
  86. package/functions/graph-composer/prompts/action-suggest-catalog-creations.md +31 -0
  87. package/functions/graph-composer/prompts/action-suggest-catalog-resolution.md +42 -0
  88. package/functions/graph-composer/prompts/action-suggest-concept-objective.md +38 -0
  89. package/functions/graph-composer/prompts/action-suggest-scoping-map-creation.md +31 -0
  90. package/functions/graph-composer/prompts/action-suggest-scoping-need-match.md +25 -0
  91. package/functions/graph-composer/prompts/default-utility-skills.json +22 -0
  92. package/functions/graph-composer/prompts/judge-rules.md +30 -0
  93. package/functions/graph-composer/prompts/orchestrator-system.md +21 -0
  94. package/functions/graph-composer/prompts/shared/graph-format.md +124 -0
  95. package/functions/graph-composer/prompts/shared/request-context.md +12 -0
  96. package/functions/graph-composer/prompts/shared/skill-selection.md +6 -0
  97. package/functions/graph-composer/prompts/shared/structural-validation.md +19 -0
  98. package/functions/graph-composer/prompts/skill-catalog-ai-header.md +3 -0
  99. package/functions/graph-composer/prompts/skill-catalog-utility-header.md +3 -0
  100. package/functions/graph-composer/prompts/skill-mode-extensible.md +7 -0
  101. package/functions/graph-composer/prompts/skill-mode-locked.md +7 -0
  102. package/functions/graph-composer/test-cases.json +52 -0
  103. package/package.json +86 -0
@@ -0,0 +1,709 @@
1
+ import { reportTaskNodeProtocolGaps } from "./aiTaskProfile.js";
2
+ import { collectCanonicalGraphWarnings } from "./canonicalGraphWarnings.js";
3
+ import { canonicalizeGraphModel, reportTaskNodeInputsLayoutIssues, taskNodeInputsLayoutWarningMessage, } from "./taskNodeTaskVariable.js";
4
+ const PRIMARY_INTENT_TYPES = new Set([
5
+ "question",
6
+ "decision",
7
+ "action",
8
+ "objective",
9
+ ]);
10
+ const CONCEPT_STATUSES = new Set([
11
+ "not-started",
12
+ "generated-draft",
13
+ "user-refined",
14
+ "complete",
15
+ ]);
16
+ const GRAPH_CONCEPT_PATCH_KEYS = [
17
+ "primaryIntentType",
18
+ "primaryIntentStatement",
19
+ "businessObjective",
20
+ "primaryOutcome",
21
+ ];
22
+ export function validateExplainOutput(out, _ctx) {
23
+ if ("graph" in out && out.graph !== undefined) {
24
+ throw new Error("explain action must not return a graph object");
25
+ }
26
+ }
27
+ function validatePresentGraphConceptPatchKeys(p, ctxPrefix) {
28
+ for (const key of GRAPH_CONCEPT_PATCH_KEYS) {
29
+ if (!(key in p))
30
+ continue;
31
+ const v = p[key];
32
+ if (key === "primaryIntentType") {
33
+ if (typeof v !== "string" || !PRIMARY_INTENT_TYPES.has(v)) {
34
+ throw new Error(`${ctxPrefix}.primaryIntentType must be one of: question, decision, action, objective`);
35
+ }
36
+ continue;
37
+ }
38
+ if (typeof v !== "string") {
39
+ throw new Error(`${ctxPrefix}.${key} must be a string`);
40
+ }
41
+ if (key === "primaryIntentStatement" && v.trim() === "") {
42
+ throw new Error(`${ctxPrefix}.primaryIntentStatement must be non-empty when present`);
43
+ }
44
+ }
45
+ }
46
+ const REVIEW_EXTRA_STRING_PATCH_KEYS = [
47
+ "graphType",
48
+ "expectedInput",
49
+ "outputDescription",
50
+ "persistenceNotes",
51
+ "notes",
52
+ "targetEntityType",
53
+ "decisionType",
54
+ ];
55
+ function validatePresentReviewConceptPatchStringExtras(p, ctxPrefix) {
56
+ for (const key of REVIEW_EXTRA_STRING_PATCH_KEYS) {
57
+ if (!(key in p))
58
+ continue;
59
+ const v = p[key];
60
+ if (typeof v !== "string") {
61
+ throw new Error(`${ctxPrefix}.${key} must be a string when present`);
62
+ }
63
+ }
64
+ }
65
+ function validateOptionalConceptStatus(v, ctxPrefix) {
66
+ if (v === undefined)
67
+ return;
68
+ if (typeof v !== "string" || !CONCEPT_STATUSES.has(v)) {
69
+ throw new Error(`${ctxPrefix}.conceptStatus must be one of: not-started, generated-draft, user-refined, complete`);
70
+ }
71
+ }
72
+ function validateOptionalEntityBindings(eb, ctxPrefix) {
73
+ if (eb === undefined)
74
+ return;
75
+ if (eb === null || typeof eb !== "object" || Array.isArray(eb)) {
76
+ throw new Error(`${ctxPrefix}.entityBindings must be a plain object when present`);
77
+ }
78
+ const o = eb;
79
+ if ("coreEntityCollectionId" in o &&
80
+ o.coreEntityCollectionId !== undefined &&
81
+ typeof o.coreEntityCollectionId !== "string") {
82
+ throw new Error(`${ctxPrefix}.entityBindings.coreEntityCollectionId must be a string when present`);
83
+ }
84
+ if ("supportingEntityCollectionIds" in o && o.supportingEntityCollectionIds !== undefined) {
85
+ const arr = o.supportingEntityCollectionIds;
86
+ if (!Array.isArray(arr)) {
87
+ throw new Error(`${ctxPrefix}.entityBindings.supportingEntityCollectionIds must be an array when present`);
88
+ }
89
+ for (let j = 0; j < arr.length; j++) {
90
+ if (typeof arr[j] !== "string") {
91
+ throw new Error(`${ctxPrefix}.entityBindings.supportingEntityCollectionIds[${j}] must be a string`);
92
+ }
93
+ }
94
+ }
95
+ if ("targetEntityCollectionId" in o &&
96
+ o.targetEntityCollectionId !== undefined &&
97
+ typeof o.targetEntityCollectionId !== "string") {
98
+ throw new Error(`${ctxPrefix}.entityBindings.targetEntityCollectionId must be a string when present`);
99
+ }
100
+ }
101
+ function validateReviewConceptRequirements(req, ctx) {
102
+ if (req === undefined)
103
+ return;
104
+ if (req === null || typeof req !== "object" || Array.isArray(req)) {
105
+ throw new Error(`${ctx} must be a plain object when present`);
106
+ }
107
+ const r = req;
108
+ if ("skill" in r && r.skill !== undefined) {
109
+ const sk = r.skill;
110
+ if (sk === null || typeof sk !== "object" || Array.isArray(sk)) {
111
+ throw new Error(`${ctx}.skill must be an object when present`);
112
+ }
113
+ const s = sk;
114
+ if (typeof s.skillKey !== "string") {
115
+ throw new Error(`${ctx}.skill.skillKey must be a string when skill is present`);
116
+ }
117
+ if ("isLocal" in s && s.isLocal !== undefined && typeof s.isLocal !== "boolean") {
118
+ throw new Error(`${ctx}.skill.isLocal must be a boolean when present`);
119
+ }
120
+ }
121
+ if ("memoryIO" in r && r.memoryIO !== undefined) {
122
+ const m = r.memoryIO;
123
+ if (m === null || typeof m !== "object" || Array.isArray(m)) {
124
+ throw new Error(`${ctx}.memoryIO must be an object when present`);
125
+ }
126
+ const mem = m;
127
+ if ("reads" in mem && mem.reads !== undefined) {
128
+ if (!Array.isArray(mem.reads)) {
129
+ throw new Error(`${ctx}.memoryIO.reads must be an array when present`);
130
+ }
131
+ for (let k = 0; k < mem.reads.length; k++) {
132
+ if (typeof mem.reads[k] !== "string") {
133
+ throw new Error(`${ctx}.memoryIO.reads[${k}] must be a string`);
134
+ }
135
+ }
136
+ }
137
+ if ("writes" in mem && mem.writes !== undefined && typeof mem.writes !== "string") {
138
+ throw new Error(`${ctx}.memoryIO.writes must be a string when present`);
139
+ }
140
+ if ("jobContextMappings" in mem &&
141
+ mem.jobContextMappings !== undefined) {
142
+ const j = mem.jobContextMappings;
143
+ if (j === null || typeof j !== "object" || Array.isArray(j)) {
144
+ throw new Error(`${ctx}.memoryIO.jobContextMappings must be an object when present`);
145
+ }
146
+ }
147
+ }
148
+ if ("strategies" in r && r.strategies !== undefined) {
149
+ const strat = r.strategies;
150
+ if (strat === null || typeof strat !== "object" || Array.isArray(strat)) {
151
+ throw new Error(`${ctx}.strategies must be an object when present`);
152
+ }
153
+ const st = strat;
154
+ for (const key of ["pre", "post"]) {
155
+ if (!(key in st) || st[key] === undefined)
156
+ continue;
157
+ const arr = st[key];
158
+ if (!Array.isArray(arr)) {
159
+ throw new Error(`${ctx}.strategies.${key} must be an array when present`);
160
+ }
161
+ for (let j = 0; j < arr.length; j++) {
162
+ if (typeof arr[j] !== "string") {
163
+ throw new Error(`${ctx}.strategies.${key}[${j}] must be a string`);
164
+ }
165
+ }
166
+ }
167
+ }
168
+ for (const sub of ["catalogBinding", "narrix"]) {
169
+ if (!(sub in r) || r[sub] === undefined)
170
+ continue;
171
+ const v = r[sub];
172
+ if (v === null || typeof v !== "object" || Array.isArray(v)) {
173
+ throw new Error(`${ctx}.${sub} must be an object when present`);
174
+ }
175
+ }
176
+ }
177
+ function validateReviewConceptCoreTaskPatchItem(t, ctxPrefix) {
178
+ if ("taskType" in t && t.taskType !== undefined) {
179
+ if (typeof t.taskType !== "string" || !PRIMARY_INTENT_TYPES.has(t.taskType)) {
180
+ throw new Error(`${ctxPrefix}.taskType must be one of: question, decision, action, objective when present`);
181
+ }
182
+ }
183
+ for (const key of ["statement", "details"]) {
184
+ if (!(key in t) || t[key] === undefined)
185
+ continue;
186
+ if (typeof t[key] !== "string") {
187
+ throw new Error(`${ctxPrefix}.${key} must be a string when present`);
188
+ }
189
+ }
190
+ if ("sourceFamilyHints" in t && t.sourceFamilyHints !== undefined) {
191
+ const hints = t.sourceFamilyHints;
192
+ if (!Array.isArray(hints)) {
193
+ throw new Error(`${ctxPrefix}.sourceFamilyHints must be an array when present`);
194
+ }
195
+ for (let j = 0; j < hints.length; j++) {
196
+ if (typeof hints[j] !== "string") {
197
+ throw new Error(`${ctxPrefix}.sourceFamilyHints[${j}] must be a string`);
198
+ }
199
+ }
200
+ }
201
+ if ("planningRunsAfterTaskIndex" in t && t.planningRunsAfterTaskIndex !== undefined) {
202
+ const p = t.planningRunsAfterTaskIndex;
203
+ if (typeof p !== "number" || !Number.isInteger(p) || p < -1) {
204
+ throw new Error(`${ctxPrefix}.planningRunsAfterTaskIndex must be an integer >= -1 when present`);
205
+ }
206
+ }
207
+ if ("stepInputs" in t && t.stepInputs !== undefined) {
208
+ const si = t.stepInputs;
209
+ if (typeof si === "string") {
210
+ /* v3 path hint */
211
+ }
212
+ else if (si === null || typeof si !== "object" || Array.isArray(si)) {
213
+ throw new Error(`${ctxPrefix}.stepInputs must be a string or plain object when present`);
214
+ }
215
+ }
216
+ if ("subTasks" in t && t.subTasks !== undefined) {
217
+ const st = t.subTasks;
218
+ if (!Array.isArray(st)) {
219
+ throw new Error(`${ctxPrefix}.subTasks must be an array when present`);
220
+ }
221
+ for (let j = 0; j < st.length; j++) {
222
+ const row = st[j];
223
+ if (row === null || typeof row !== "object" || Array.isArray(row)) {
224
+ throw new Error(`${ctxPrefix}.subTasks[${j}] must be an object`);
225
+ }
226
+ }
227
+ }
228
+ if ("webScoping" in t && t.webScoping !== undefined) {
229
+ const w = t.webScoping;
230
+ if (w === null || typeof w !== "object" || Array.isArray(w)) {
231
+ throw new Error(`${ctxPrefix}.webScoping must be an object when present`);
232
+ }
233
+ }
234
+ if ("synthesis" in t && t.synthesis !== undefined) {
235
+ const syn = t.synthesis;
236
+ if (syn === null || typeof syn !== "object" || Array.isArray(syn)) {
237
+ throw new Error(`${ctxPrefix}.synthesis must be an object when present`);
238
+ }
239
+ }
240
+ if ("requirements" in t && t.requirements !== undefined) {
241
+ validateReviewConceptRequirements(t.requirements, `${ctxPrefix}.requirements`);
242
+ }
243
+ }
244
+ function validateReviewConceptPatchObject(patch, ctxPrefix) {
245
+ validatePresentGraphConceptPatchKeys(patch, ctxPrefix);
246
+ validatePresentReviewConceptPatchStringExtras(patch, ctxPrefix);
247
+ if ("conceptStatus" in patch) {
248
+ validateOptionalConceptStatus(patch.conceptStatus, ctxPrefix);
249
+ }
250
+ if ("entityBindings" in patch) {
251
+ validateOptionalEntityBindings(patch.entityBindings, ctxPrefix);
252
+ }
253
+ const ct = patch.coreTasks;
254
+ if (ct === undefined)
255
+ return;
256
+ if (!Array.isArray(ct)) {
257
+ throw new Error(`${ctxPrefix}.coreTasks must be an array when present`);
258
+ }
259
+ for (let i = 0; i < ct.length; i++) {
260
+ const item = ct[i];
261
+ if (item === null || item === undefined)
262
+ continue;
263
+ if (typeof item !== "object" || Array.isArray(item)) {
264
+ throw new Error(`${ctxPrefix}.coreTasks[${i}] must be an object or null`);
265
+ }
266
+ const t = item;
267
+ validateReviewConceptCoreTaskPatchItem(t, `${ctxPrefix}.coreTasks[${i}]`);
268
+ }
269
+ }
270
+ function validateOptionalReviewConceptPatch(patch, fieldName) {
271
+ if (patch === undefined || patch === null)
272
+ return;
273
+ if (typeof patch !== "object" || Array.isArray(patch)) {
274
+ throw new Error(`${fieldName} must be an object when present`);
275
+ }
276
+ validateReviewConceptPatchObject(patch, fieldName);
277
+ }
278
+ function requirementOptionCandidateHasContent(o) {
279
+ if (typeof o.value === "string" && o.value.length > 0)
280
+ return true;
281
+ if (typeof o.rationale === "string" &&
282
+ o.rationale.trim() !== "") {
283
+ return true;
284
+ }
285
+ return false;
286
+ }
287
+ function validateRequirementOptionsArray(arr, ctx) {
288
+ if (!Array.isArray(arr)) {
289
+ throw new Error(`${ctx} must be an array when present`);
290
+ }
291
+ for (let i = 0; i < arr.length; i++) {
292
+ const row = arr[i];
293
+ if (row === null || typeof row !== "object" || Array.isArray(row)) {
294
+ throw new Error(`${ctx}[${i}] must be an object`);
295
+ }
296
+ const o = row;
297
+ if (typeof o.taskIndex !== "number" ||
298
+ !Number.isInteger(o.taskIndex) ||
299
+ o.taskIndex < 0) {
300
+ throw new Error(`${ctx}[${i}].taskIndex must be a non-negative integer`);
301
+ }
302
+ if (typeof o.field !== "string" || o.field.trim() === "") {
303
+ throw new Error(`${ctx}[${i}].field must be a non-empty string`);
304
+ }
305
+ if (typeof o.needsNewArtifact !== "boolean") {
306
+ throw new Error(`${ctx}[${i}].needsNewArtifact must be a boolean`);
307
+ }
308
+ const cand = o.candidates;
309
+ if (!Array.isArray(cand) || cand.length === 0) {
310
+ throw new Error(`${ctx}[${i}].candidates must be a non-empty array`);
311
+ }
312
+ for (let j = 0; j < cand.length; j++) {
313
+ const c = cand[j];
314
+ if (c === null || typeof c !== "object" || Array.isArray(c)) {
315
+ throw new Error(`${ctx}[${i}].candidates[${j}] must be an object`);
316
+ }
317
+ const co = c;
318
+ if (!requirementOptionCandidateHasContent(co)) {
319
+ throw new Error(`${ctx}[${i}].candidates[${j}] must include a non-empty string value or non-empty rationale`);
320
+ }
321
+ if ("value" in co && co.value !== undefined && typeof co.value !== "string") {
322
+ throw new Error(`${ctx}[${i}].candidates[${j}].value must be a string when present`);
323
+ }
324
+ if ("rationale" in co &&
325
+ co.rationale !== undefined &&
326
+ typeof co.rationale !== "string") {
327
+ throw new Error(`${ctx}[${i}].candidates[${j}].rationale must be a string when present`);
328
+ }
329
+ if ("source" in co && co.source !== undefined && typeof co.source !== "string") {
330
+ throw new Error(`${ctx}[${i}].candidates[${j}].source must be a string when present`);
331
+ }
332
+ }
333
+ }
334
+ }
335
+ export function validateReviewConceptOutput(out, _ctx) {
336
+ assertNoGraph(out, "reviewConcept");
337
+ if ("changelog" in out && out.changelog !== undefined) {
338
+ throw new Error("reviewConcept action must not return a changelog");
339
+ }
340
+ const verdict = out.verdict;
341
+ if (verdict === null ||
342
+ verdict === undefined ||
343
+ typeof verdict !== "object" ||
344
+ Array.isArray(verdict)) {
345
+ throw new Error("reviewConcept output must include verdict object");
346
+ }
347
+ const v = verdict;
348
+ if (typeof v.coherent !== "boolean") {
349
+ throw new Error("reviewConcept.verdict.coherent must be a boolean");
350
+ }
351
+ if (typeof v.summary !== "string" || v.summary.trim() === "") {
352
+ throw new Error("reviewConcept.verdict.summary must be a non-empty string");
353
+ }
354
+ if (!Array.isArray(out.findings)) {
355
+ throw new Error("reviewConcept output must include findings array");
356
+ }
357
+ const findings = out.findings;
358
+ for (let i = 0; i < findings.length; i++) {
359
+ const row = findings[i];
360
+ if (row === null || typeof row !== "object" || Array.isArray(row)) {
361
+ throw new Error(`findings[${i}] must be an object`);
362
+ }
363
+ const f = row;
364
+ assertStringField(f, "category", `findings[${i}]`);
365
+ assertStringField(f, "severity", `findings[${i}]`);
366
+ assertStringField(f, "summary", `findings[${i}]`);
367
+ if (f.nodeIds !== undefined) {
368
+ if (!Array.isArray(f.nodeIds)) {
369
+ throw new Error(`findings[${i}].nodeIds must be an array when present`);
370
+ }
371
+ for (let j = 0; j < f.nodeIds.length; j++) {
372
+ const id = f.nodeIds[j];
373
+ if (typeof id !== "string") {
374
+ throw new Error(`findings[${i}].nodeIds[${j}] must be a string`);
375
+ }
376
+ }
377
+ }
378
+ if (f.suggestedChange !== undefined &&
379
+ typeof f.suggestedChange !== "string") {
380
+ throw new Error(`findings[${i}].suggestedChange must be a string when present`);
381
+ }
382
+ if (f.taskIndex !== undefined) {
383
+ if (typeof f.taskIndex !== "number" ||
384
+ !Number.isInteger(f.taskIndex) ||
385
+ f.taskIndex < 0) {
386
+ throw new Error(`findings[${i}].taskIndex must be a non-negative integer when present`);
387
+ }
388
+ }
389
+ if (f.proposal !== undefined) {
390
+ if (f.proposal === null ||
391
+ typeof f.proposal !== "object" ||
392
+ Array.isArray(f.proposal)) {
393
+ throw new Error(`findings[${i}].proposal must be a plain object when present`);
394
+ }
395
+ }
396
+ }
397
+ validateOptionalReviewConceptPatch(out.graphConceptPatch, "graphConceptPatch");
398
+ validateOptionalReviewConceptPatch(out.suggestedConceptPatch, "suggestedConceptPatch");
399
+ if (out.catalogProposals !== undefined) {
400
+ if (!Array.isArray(out.catalogProposals)) {
401
+ throw new Error("catalogProposals must be an array when present");
402
+ }
403
+ const props = out.catalogProposals;
404
+ for (let i = 0; i < props.length; i++) {
405
+ const row = props[i];
406
+ if (row === null || typeof row !== "object" || Array.isArray(row)) {
407
+ throw new Error(`catalogProposals[${i}] must be an object`);
408
+ }
409
+ }
410
+ }
411
+ if (out.requirementOptions !== undefined) {
412
+ validateRequirementOptionsArray(out.requirementOptions, "requirementOptions");
413
+ }
414
+ }
415
+ /**
416
+ * When `existingGraph.metadata.graphConcept.coreTasks` has length N > 0, any
417
+ * `graphConceptPatch` / `suggestedConceptPatch` must include `coreTasks` with
418
+ * the same length (index-aligned). Call after `validateReviewConceptOutput`.
419
+ */
420
+ export function validateReviewConceptOutputAgainstInput(out, input) {
421
+ const n = coreTasksLengthFromExistingGraph(input.existingGraph);
422
+ if (n === undefined || n <= 0)
423
+ return;
424
+ const gcp = out.graphConceptPatch;
425
+ const scp = out.suggestedConceptPatch;
426
+ const hasGcp = gcp !== null &&
427
+ gcp !== undefined &&
428
+ typeof gcp === "object" &&
429
+ !Array.isArray(gcp);
430
+ const hasScp = scp !== null &&
431
+ scp !== undefined &&
432
+ typeof scp === "object" &&
433
+ !Array.isArray(scp);
434
+ if (!hasGcp && !hasScp)
435
+ return;
436
+ function checkPatch(patch, fieldName) {
437
+ const ct = patch.coreTasks;
438
+ if (!Array.isArray(ct)) {
439
+ throw new Error(`reviewConcept: when existingGraph.metadata.graphConcept.coreTasks has length ${n}, ${fieldName} must include coreTasks as an array of length ${n}`);
440
+ }
441
+ if (ct.length !== n) {
442
+ throw new Error(`reviewConcept: ${fieldName}.coreTasks must have length ${n} to match existing concept (got ${ct.length})`);
443
+ }
444
+ }
445
+ if (hasGcp)
446
+ checkPatch(gcp, "graphConceptPatch");
447
+ if (hasScp)
448
+ checkPatch(scp, "suggestedConceptPatch");
449
+ }
450
+ function coreTasksLengthFromExistingGraph(existingGraph) {
451
+ if (existingGraph === null ||
452
+ typeof existingGraph !== "object" ||
453
+ Array.isArray(existingGraph)) {
454
+ return undefined;
455
+ }
456
+ const meta = existingGraph.metadata;
457
+ if (meta === null || typeof meta !== "object" || Array.isArray(meta)) {
458
+ return undefined;
459
+ }
460
+ const gc = meta.graphConcept;
461
+ if (gc === null || typeof gc !== "object" || Array.isArray(gc)) {
462
+ return undefined;
463
+ }
464
+ const ct = gc.coreTasks;
465
+ if (!Array.isArray(ct))
466
+ return undefined;
467
+ return ct.length;
468
+ }
469
+ export function validateSuggestConceptOutput(out, _ctx) {
470
+ if ("graph" in out && out.graph !== undefined) {
471
+ throw new Error("suggestConceptObjective action must not return a graph object");
472
+ }
473
+ const patch = out.graphConceptPatch;
474
+ if (patch === undefined ||
475
+ patch === null ||
476
+ typeof patch !== "object" ||
477
+ Array.isArray(patch)) {
478
+ throw new Error("suggestConceptObjective output must include graphConceptPatch object");
479
+ }
480
+ const p = patch;
481
+ validatePresentGraphConceptPatchKeys(p, "graphConceptPatch");
482
+ }
483
+ function appendWarnings(out, messages) {
484
+ if (messages.length === 0)
485
+ return;
486
+ const existing = out.warnings;
487
+ if (Array.isArray(existing)) {
488
+ out.warnings = [...existing, ...messages];
489
+ }
490
+ else {
491
+ out.warnings = messages;
492
+ }
493
+ }
494
+ function appendCreateModifyLayoutWarnings(out, graph) {
495
+ const issues = reportTaskNodeInputsLayoutIssues(graph);
496
+ if (issues.length === 0)
497
+ return;
498
+ const msg = taskNodeInputsLayoutWarningMessage(issues);
499
+ appendWarnings(out, [msg]);
500
+ }
501
+ export function validateCreateModifyOutput(out, ctx) {
502
+ if (!Array.isArray(out.changelog)) {
503
+ throw new Error("create/modify output must include changelog array");
504
+ }
505
+ const graph = out.graph;
506
+ if (graph !== null &&
507
+ graph !== undefined &&
508
+ typeof graph === "object" &&
509
+ !Array.isArray(graph)) {
510
+ appendCreateModifyLayoutWarnings(out, graph);
511
+ const canonicalized = canonicalizeGraphModel(graph);
512
+ const gaps = reportTaskNodeProtocolGaps(canonicalized, {
513
+ utilitySkills: ctx?.input?.utilitySkills,
514
+ });
515
+ if (gaps.length > 0) {
516
+ throw new Error(`create/modify graph violates task node protocol (taskConfiguration.aiTaskProfile / webScoping / inputSynthesis): ${JSON.stringify(gaps)}`);
517
+ }
518
+ appendWarnings(out, collectCanonicalGraphWarnings(canonicalized));
519
+ out.graph = canonicalized;
520
+ }
521
+ }
522
+ function assertNoGraph(out, action) {
523
+ if ("graph" in out && out.graph !== undefined) {
524
+ throw new Error(`${action} action must not return a graph object`);
525
+ }
526
+ }
527
+ function assertStringField(row, field, ctx) {
528
+ const v = row[field];
529
+ if (typeof v !== "string" || v.trim() === "") {
530
+ throw new Error(`${ctx}: ${field} must be a non-empty string`);
531
+ }
532
+ }
533
+ export function validateSuggestCatalogResolutionOutput(out, _ctx) {
534
+ assertNoGraph(out, "suggestCatalogResolution");
535
+ for (const key of ["skillResolution", "scopingResolution", "narrixResolution", "gaps"]) {
536
+ if (!Array.isArray(out[key])) {
537
+ throw new Error(`suggestCatalogResolution output must include ${key} array`);
538
+ }
539
+ }
540
+ const skills = out.skillResolution;
541
+ for (let i = 0; i < skills.length; i++) {
542
+ const row = skills[i];
543
+ if (row === null || typeof row !== "object" || Array.isArray(row)) {
544
+ throw new Error(`skillResolution[${i}] must be an object`);
545
+ }
546
+ const o = row;
547
+ assertStringField(o, "nodeId", `skillResolution[${i}]`);
548
+ assertStringField(o, "skillKey", `skillResolution[${i}]`);
549
+ if ("isLocal" in o && typeof o.isLocal !== "boolean") {
550
+ throw new Error(`skillResolution[${i}].isLocal must be boolean when present`);
551
+ }
552
+ }
553
+ const scoping = out.scopingResolution;
554
+ for (let i = 0; i < scoping.length; i++) {
555
+ const row = scoping[i];
556
+ if (row === null || typeof row !== "object" || Array.isArray(row)) {
557
+ throw new Error(`scopingResolution[${i}] must be an object`);
558
+ }
559
+ }
560
+ const narrix = out.narrixResolution;
561
+ for (let i = 0; i < narrix.length; i++) {
562
+ const row = narrix[i];
563
+ if (row === null || typeof row !== "object" || Array.isArray(row)) {
564
+ throw new Error(`narrixResolution[${i}] must be an object`);
565
+ }
566
+ }
567
+ const gaps = out.gaps;
568
+ for (let i = 0; i < gaps.length; i++) {
569
+ const row = gaps[i];
570
+ if (row === null || typeof row !== "object" || Array.isArray(row)) {
571
+ throw new Error(`gaps[${i}] must be an object`);
572
+ }
573
+ const o = row;
574
+ assertStringField(o, "kind", `gaps[${i}]`);
575
+ assertStringField(o, "summary", `gaps[${i}]`);
576
+ }
577
+ }
578
+ export function validateSuggestCatalogCreationsOutput(out, _ctx) {
579
+ assertNoGraph(out, "suggestCatalogCreations");
580
+ if (!Array.isArray(out.catalogRequestProposals)) {
581
+ throw new Error("suggestCatalogCreations output must include catalogRequestProposals array");
582
+ }
583
+ const proposals = out.catalogRequestProposals;
584
+ for (let i = 0; i < proposals.length; i++) {
585
+ const row = proposals[i];
586
+ if (row === null || typeof row !== "object" || Array.isArray(row)) {
587
+ throw new Error(`catalogRequestProposals[${i}] must be an object`);
588
+ }
589
+ const o = row;
590
+ assertStringField(o, "catalogType", `catalogRequestProposals[${i}]`);
591
+ assertStringField(o, "requestedId", `catalogRequestProposals[${i}]`);
592
+ }
593
+ if (out.newSkillDescriptors !== undefined) {
594
+ if (!Array.isArray(out.newSkillDescriptors)) {
595
+ throw new Error("newSkillDescriptors must be an array when present");
596
+ }
597
+ const nd = out.newSkillDescriptors;
598
+ for (let i = 0; i < nd.length; i++) {
599
+ const row = nd[i];
600
+ if (row === null || typeof row !== "object" || Array.isArray(row)) {
601
+ throw new Error(`newSkillDescriptors[${i}] must be an object`);
602
+ }
603
+ const o = row;
604
+ assertStringField(o, "skillKey", `newSkillDescriptors[${i}]`);
605
+ assertStringField(o, "description", `newSkillDescriptors[${i}]`);
606
+ }
607
+ }
608
+ }
609
+ function recommendedScopingHasId(o) {
610
+ const sid = o.scopingMapId;
611
+ const qid = o.questionId;
612
+ const hasSid = typeof sid === "string" && sid.trim() !== "";
613
+ const hasQid = typeof qid === "string" && qid.trim() !== "";
614
+ return hasSid || hasQid;
615
+ }
616
+ export function validateSuggestScopingNeedMatchOutput(out, _ctx) {
617
+ assertNoGraph(out, "suggestScopingNeedMatch");
618
+ if (typeof out.hasSuitableMatch !== "boolean") {
619
+ throw new Error("suggestScopingNeedMatch output must include boolean hasSuitableMatch");
620
+ }
621
+ if (!Array.isArray(out.gaps)) {
622
+ throw new Error("suggestScopingNeedMatch output must include gaps array");
623
+ }
624
+ const gaps = out.gaps;
625
+ for (let i = 0; i < gaps.length; i++) {
626
+ const row = gaps[i];
627
+ if (row === null || typeof row !== "object" || Array.isArray(row)) {
628
+ throw new Error(`gaps[${i}] must be an object`);
629
+ }
630
+ const g = row;
631
+ assertStringField(g, "kind", `gaps[${i}]`);
632
+ assertStringField(g, "summary", `gaps[${i}]`);
633
+ }
634
+ if (!("recommended" in out)) {
635
+ throw new Error("suggestScopingNeedMatch output must include recommended key");
636
+ }
637
+ const rec = out.recommended;
638
+ if (out.hasSuitableMatch) {
639
+ if (rec === null || typeof rec !== "object" || Array.isArray(rec)) {
640
+ throw new Error("when hasSuitableMatch is true, recommended must be a non-null object");
641
+ }
642
+ if (!recommendedScopingHasId(rec)) {
643
+ throw new Error("when hasSuitableMatch is true, recommended must include non-empty scopingMapId and/or questionId");
644
+ }
645
+ }
646
+ else {
647
+ if (rec !== null) {
648
+ throw new Error("when hasSuitableMatch is false, recommended must be null");
649
+ }
650
+ }
651
+ if (out.alternatives !== undefined) {
652
+ if (!Array.isArray(out.alternatives)) {
653
+ throw new Error("alternatives must be an array when present");
654
+ }
655
+ const alts = out.alternatives;
656
+ for (let i = 0; i < alts.length; i++) {
657
+ const row = alts[i];
658
+ if (row === null || typeof row !== "object" || Array.isArray(row)) {
659
+ throw new Error(`alternatives[${i}] must be an object`);
660
+ }
661
+ }
662
+ }
663
+ }
664
+ export function validateSuggestScopingMapCreationOutput(out, _ctx) {
665
+ assertNoGraph(out, "suggestScopingMapCreation");
666
+ const prop = out.scopingMapProposal;
667
+ if (prop === null ||
668
+ prop === undefined ||
669
+ typeof prop !== "object" ||
670
+ Array.isArray(prop)) {
671
+ throw new Error("suggestScopingMapCreation output must include scopingMapProposal object");
672
+ }
673
+ const p = prop;
674
+ assertStringField(p, "suggestedScopingMapId", "scopingMapProposal");
675
+ assertStringField(p, "title", "scopingMapProposal");
676
+ assertStringField(p, "purpose", "scopingMapProposal");
677
+ if ("entityIdPath" in p && p.entityIdPath !== undefined) {
678
+ if (typeof p.entityIdPath !== "string") {
679
+ throw new Error("scopingMapProposal.entityIdPath must be a string when present");
680
+ }
681
+ }
682
+ if ("expectedFieldPaths" in p && p.expectedFieldPaths !== undefined) {
683
+ if (!Array.isArray(p.expectedFieldPaths)) {
684
+ throw new Error("scopingMapProposal.expectedFieldPaths must be an array when present");
685
+ }
686
+ for (let i = 0; i < p.expectedFieldPaths.length; i++) {
687
+ const v = p.expectedFieldPaths[i];
688
+ if (typeof v !== "string") {
689
+ throw new Error(`scopingMapProposal.expectedFieldPaths[${i}] must be a string`);
690
+ }
691
+ }
692
+ }
693
+ if (!Array.isArray(out.catalogRequestProposals)) {
694
+ throw new Error("suggestScopingMapCreation output must include catalogRequestProposals array");
695
+ }
696
+ const proposals = out.catalogRequestProposals;
697
+ if (proposals.length < 1) {
698
+ throw new Error("catalogRequestProposals must be non-empty");
699
+ }
700
+ for (let i = 0; i < proposals.length; i++) {
701
+ const row = proposals[i];
702
+ if (row === null || typeof row !== "object" || Array.isArray(row)) {
703
+ throw new Error(`catalogRequestProposals[${i}] must be an object`);
704
+ }
705
+ const o = row;
706
+ assertStringField(o, "catalogType", `catalogRequestProposals[${i}]`);
707
+ assertStringField(o, "requestedId", `catalogRequestProposals[${i}]`);
708
+ }
709
+ }